我正在尝试使用SSE4点阵产品改进此代码,但我很难找到解决方案.该函数获取包含具有80个单元格的浮点数组的参数qi和tj,然后计算点积.返回值是具有四个点积的向量.所以我要做的就是平行计算二十个值的四个点积.
你有什么想法如何改进这个代码?
inline __m128 ScalarProd20Vec(__m128* qi,__m128* tj) { __m128 res=_mm_add_ps(_mm_mul_ps(tj[0],qi[0]),_mm_mul_ps(tj[1],qi[1])); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[2],qi[2]),_mm_mul_ps(tj[3],qi[3]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[4],qi[4]),_mm_mul_ps(tj[5],qi[5]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[6],qi[6]),_mm_mul_ps(tj[7],qi[7]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[8],qi[8]),_mm_mul_ps(tj[9],qi[9]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[10],qi[10]),_mm_mul_ps(tj[11],qi[11]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[12],qi[12]),_mm_mul_ps(tj[13],qi[13]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[14],qi[14]),_mm_mul_ps(tj[15],qi[15]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[16],qi[16]),_mm_mul_ps(tj[17],qi[17]))); res=_mm_add_ps(res,_mm_add_ps(_mm_mul_ps(tj[18],qi[18]),_mm_mul_ps(tj[19],qi[19]))); return res; }
解决方法
在我看到的数以百计的SSE示例中,您的代码是从一开始就已经处于非常好的形状的少数几个.您不需要SSE4点阵产品说明. (你可以做得更好!)
但是,有一件事你可以尝试:(我说尝试,因为我还没有计时.)
目前,您有一个数据依赖链.目前,大多数机器的向量加法是3-4个周期.因此,您的代码至少需要30个周期才能运行:
(10 additions on critical path) * (3 cycles addps latency) = 30 cycles
你可以做的是将res变量node-split,如下所示:
__m128 res0 = _mm_add_ps(_mm_mul_ps(tj[ 0],qi[ 0]),_mm_mul_ps(tj[ 1],qi[ 1])); __m128 res1 = _mm_add_ps(_mm_mul_ps(tj[ 2],qi[ 2]),_mm_mul_ps(tj[ 3],qi[ 3])); res0 = _mm_add_ps(res0,_mm_add_ps(_mm_mul_ps(tj[ 4],qi[ 4]),_mm_mul_ps(tj[ 5],qi[ 5]))); res1 = _mm_add_ps(res1,_mm_add_ps(_mm_mul_ps(tj[ 6],qi[ 6]),_mm_mul_ps(tj[ 7],qi[ 7]))); res0 = _mm_add_ps(res0,_mm_add_ps(_mm_mul_ps(tj[ 8],qi[ 8]),_mm_mul_ps(tj[ 9],qi[ 9]))); res1 = _mm_add_ps(res1,qi[11]))); res0 = _mm_add_ps(res0,qi[13]))); res1 = _mm_add_ps(res1,qi[15]))); res0 = _mm_add_ps(res0,qi[17]))); res1 = _mm_add_ps(res1,qi[19]))); return _mm_add_ps(res0,res1);
这几乎将你的关键路径削减了一半.请注意,由于浮点非关联性,这种优化对于编译器来说是非法的.
这是使用4路节点拆分和AMD FMA4指令的替代版本.如果您不能使用融合乘法添加,请随意拆分.它可能仍然比上面的第一个版本更好.
__m128 res0 = _mm_mul_ps(tj[ 0],qi[ 0]); __m128 res1 = _mm_mul_ps(tj[ 1],qi[ 1]); __m128 res2 = _mm_mul_ps(tj[ 2],qi[ 2]); __m128 res3 = _mm_mul_ps(tj[ 3],qi[ 3]); res0 = _mm_macc_ps(tj[ 4],qi[ 4],res0); res1 = _mm_macc_ps(tj[ 5],qi[ 5],res1); res2 = _mm_macc_ps(tj[ 6],qi[ 6],res2); res3 = _mm_macc_ps(tj[ 7],qi[ 7],res3); res0 = _mm_macc_ps(tj[ 8],qi[ 8],res0); res1 = _mm_macc_ps(tj[ 9],qi[ 9],res1); res2 = _mm_macc_ps(tj[10],qi[10],res2); res3 = _mm_macc_ps(tj[11],qi[11],res3); res0 = _mm_macc_ps(tj[12],qi[12],res0); res1 = _mm_macc_ps(tj[13],qi[13],res1); res2 = _mm_macc_ps(tj[14],qi[14],res2); res3 = _mm_macc_ps(tj[15],qi[15],res3); res0 = _mm_macc_ps(tj[16],qi[16],res0); res1 = _mm_macc_ps(tj[17],qi[17],res1); res2 = _mm_macc_ps(tj[18],qi[18],res2); res3 = _mm_macc_ps(tj[19],qi[19],res3); res0 = _mm_add_ps(res0,res1); res2 = _mm_add_ps(res2,res3); return _mm_add_ps(res0,res2);