我目前正在尝试对以下代码进行矢量化:
velocity[0] = 0.0;
velocity[1] = 0.0;
velocity[2] = 0.0;
for (int i = 0; i < PARAMQ; i++)
{
velocity[0] += currentCell[i] * LATTICEVELOCITIES[i][0];
velocity[1] += currentCell[i] * LATTICEVELOCITIES[i][1];
velocity[2] += currentCell[i] * LATTICEVELOCITIES[i][2];
}
其中LATTICEVELOCITIES是一个二维整数数组
static const int32_t LATTICEVELOCITIES[PARAMQ][3] = {{0, -1, -1},
{-1, 0, -1},
{0, 0, -1},
{1, 0, -1},
{0, 1, -1},
{-1, -1, 0},
{0, -1, 0},
{1, -1, 0},
{-1, 0, 0},
{0, 0, 0},
{1, 0, 0},
{-1, 1, 0},
{0, 1, 0},
{1, 1, 0},
{0, -1, 1},
{-1, 0, 1},
{0, 0, 1},
{1, 0, 1},
{0, 1, 1}
};
和currentCell是一个双打数组。
不幸的是,我找到的所有示例只处理相同类型的数组,我不知道如何只将两个整数加载到一个128位寄存器中,然后将它们转换为双精度。
提前感谢您的帮助。
答案 0 :(得分:1)
首先,根据上面的评论,我假设可以转置LATTICEVELOCITIES
:
static const int32_t LATTICEVELOCITIES[3][PARAMQ] = {
{ 0, -1, 0, 1, 0, -1, 0, 1, -1, 0, 1, -1, 0, 1, 0, -1, 0, 1, 0 },
{ -1, 0, 0, 0, 1, -1, -1, -1, 0, 0, 0, 1, 1, 1, -1, 0, 0, 0, 1 },
{ -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }
};
现在让我们遍历数据,每次迭代处理两个元素,并在结尾处进行单个标量迭代以处理最后一个(奇数)元素:
__m128d v0, v2, v2;
v0 = v1 = v2 = _mm_setzero_pd();
for (int i = 0; i < PARAMQ - 1; i += 2)
{
__m128d vc, vl0, vl1, vl2;
__m128i vtemp;
vc = _mm_loadu_pd(¤tCell[i]);
vtemp = _mm_loadu_si128((__m128i *)&LATTICEVELOCITIES[0][i]);
vl0 = _mm_cvtepi32_pd(vtemp);
vtemp = _mm_loadu_si128((__m128i *)&LATTICEVELOCITIES[1][i]);
vl1 = _mm_cvtepi32_pd(vtemp);
vtemp = _mm_loadu_si128((__m128i *)&LATTICEVELOCITIES[2][i]);
vl2 = _mm_cvtepi32_pd(vtemp);
v0 = _mm_add_pd(v0, _mm_mul_pd(vc, vl0));
v1 = _mm_add_pd(v1, _mm_mul_pd(vc, vl1));
v2 = _mm_add_pd(v2, _mm_mul_pd(vc, vl2));
}
v0 = _mm_hadd_pd(v0, v0);
v1 = _mm_hadd_pd(v1, v1);
v2 = _mm_hadd_pd(v2, v2);
_mm_store_sd(&velocity[0], v0);
_mm_store_sd(&velocity[1], v1);
_mm_store_sd(&velocity[2], v2);
if (i < PARAMQ)
{
velocity[0] += currentCell[i] * LATTICEVELOCITIES[0][i];
velocity[1] += currentCell[i] * LATTICEVELOCITIES[1][i];
velocity[2] += currentCell[i] * LATTICEVELOCITIES[2][i];
}
请注意,这是完全未经测试的代码 - 对于需要修复的拼写错误或错误道歉,但基本想法应该是合理的。
另请注意,您应该针对等效的标量代码对其进行测试和基准测试 - 现代CPU通常具有两个FPU,因此SSE可能无法获得太多。如果您可以假设Sandy Bridge / Ivy Bridge / Haswell或更高版本,那么AVX / AVX2实现应该做得更好。