SSE2:将2d数组中的有符号整数与双精度相乘,并用C语言对结果求和

SSE2:将2d数组中的有符号整数与双精度相乘,并用C语言对结果求和,c,x86,sse,simd,sse2,C,X86,Sse,Simd,Sse2,我目前正在尝试将以下代码矢量化: 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]

我目前正在尝试将以下代码矢量化:

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];
}
currentCell是一个双精度数组

不幸的是,我能找到的所有示例都只处理相同类型的数组,我不知道如何将两个整数加载到一个128位寄存器中,然后将它们转换为双精度


提前感谢您的帮助。

首先,根据上面的评论,我假设可以转置
latticesvelocies

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 }
};
现在让我们遍历数据,每次迭代处理两个元素,并在最后进行一次标量迭代以处理最后一个(奇数)元素:

\uuuum128d v0、v2、v2;
v0=v1=v2=_mm_setzero_pd();
对于(int i=0;i
请注意,这是完全未经测试的代码-为需要修复的打字错误或bug道歉,但基本思想应该是合理的


还请注意,您应该根据等效的标量代码对其进行测试和基准测试-现代CPU通常有两个FPU,因此可能无法从SSE中获得太多好处。如果您可以假设Sandy Bridge/Ivy Bridge/Haswell或更高版本,那么AVX/AVX2实现应该会更好。

您的数据组织对于矢量化来说是次优的-您能改变它吗?我不想改变它,但如果有必要,我可以。你有什么建议?如果你能添加
latticesvelocies
的完整定义,那会有所帮助-我假设它类似于
int32_t latticesvelocies[PARAMQ][3]?我编辑了原始帖子,现在包含了定义。好的-谢谢-我们可以将定义更改为:
静态常数双晶格速度[PARAMQ][4]
(第四个元素只是一个虚拟元素),或者更好的是:
静态常量双格速度[3][PARAMQ]?非常感谢!它运行良好,节省了20秒的计算时间。我还不熟悉AVX,但我可能会看看它。
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(&currentCell[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];
}