我需要使用SSE2编写一个点积(没有_mm_dp_ps也没有_mm_hadd_ps):
#include <xmmintrin.h>
inline __m128 sse_dot4(__m128 a, __m128 b)
{
const __m128 mult = _mm_mul_ps(a, b);
const __m128 shuf1 = _mm_shuffle_ps(mult, mult, _MM_SHUFFLE(0, 3, 2, 1));
const __m128 shuf2 = _mm_shuffle_ps(mult,mult, _MM_SHUFFLE(1, 0, 3, 2));
const __m128 shuf3 = _mm_shuffle_ps(mult,mult, _MM_SHUFFLE(2, 1, 0, 3));
return _mm_add_ss(_mm_add_ss(_mm_add_ss(mult, shuf1), shuf2), shuf3);
}
但是我用gcc 4.9(实验性)-O3查看了生成的汇编程序,得到了:
mulps %xmm1, %xmm0
movaps %xmm0, %xmm3 //These lines
movaps %xmm0, %xmm2 //have no use
movaps %xmm0, %xmm1 //isn't it ?
shufps $57, %xmm0, %xmm3
shufps $78, %xmm0, %xmm2
shufps $147, %xmm0, %xmm1
addss %xmm3, %xmm0
addss %xmm2, %xmm0
addss %xmm1, %xmm0
ret
我想知道为什么gcc在xmm1,2和3中复制xmm0 ... 这是我使用标志获得的代码:-march = native(看起来更好)
vmulps %xmm1, %xmm0, %xmm1
vshufps $78, %xmm1, %xmm1, %xmm2
vshufps $57, %xmm1, %xmm1, %xmm3
vshufps $147, %xmm1, %xmm1, %xmm0
vaddss %xmm3, %xmm1, %xmm1
vaddss %xmm2, %xmm1, %xmm1
vaddss %xmm0, %xmm1, %xmm0
ret
答案 0 :(得分:5)
这是一个只使用原始SSE指令的点积,它也会在每个元素上调整结果:
inline __m128 sse_dot4(__m128 v0, __m128 v1)
{
v0 = _mm_mul_ps(v0, v1);
v1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(2, 3, 0, 1));
v0 = _mm_add_ps(v0, v1);
v1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(0, 1, 2, 3));
v0 = _mm_add_ps(v0, v1);
return v0;
}
这是5条SIMD指令(而不是7条),但没有隐藏延迟的真正机会。任何元素都会保留结果,例如float f = _mm_cvtss_f32(sse_dot4(a, b);
haddps
指令的延迟非常糟糕。使用SSE3:
inline __m128 sse_dot4(__m128 v0, __m128 v1)
{
v0 = _mm_mul_ps(v0, v1);
v0 = _mm_hadd_ps(v0, v0);
v0 = _mm_hadd_ps(v0, v0);
return v0;
}
虽然只有3条SIMD指令,但这可能会慢一些。如果您一次可以执行多个点积,则可以在第一种情况下交错指令。在最近的微架构中,Shuffle非常快。
答案 1 :(得分:4)
您粘贴的第一个列表仅适用于SSE体系结构。大多数SSE指令仅支持两种操作数语法:指令采用a = a OP b
。
在您的代码中,a
为mult
。因此,如果没有复制并直接传递mult
(在您的示例中为xmm0
),其值将被覆盖,然后在剩余的_mm_shuffle_ps
指令中丢失
通过在第二个列表中传递march=native
,您启用了AVX指令。 AVX使SSE intructions能够使用三个操作数语法:c = a OP b
。在这种情况下,不必覆盖任何源操作数,因此您不需要额外的副本。
答案 2 :(得分:4)
让我建议,如果您打算使用SIMD做点积,那么您可以尝试找到一种方法同时对多个向量进行操作。例如,对于SSE,如果你有四个向量,并且你想要使用固定向量的点积,那么你排列数据,如(xxxx),(yyyy),(zzzz),(wwww),并添加每个SSE向量,并得到四个点产品的结果一次。这将使您获得100%(四倍的加速)效率,并且不仅限于4分量矢量,它对于n分量矢量也是100%有效的。这是一个仅使用SSE的示例。
#include <xmmintrin.h>
#include <stdio.h>
void dot4x4(float *aosoa, float *b, float *out) {
__m128 vx = _mm_load_ps(&aosoa[0]);
__m128 vy = _mm_load_ps(&aosoa[4]);
__m128 vz = _mm_load_ps(&aosoa[8]);
__m128 vw = _mm_load_ps(&aosoa[12]);
__m128 brod1 = _mm_set1_ps(b[0]);
__m128 brod2 = _mm_set1_ps(b[1]);
__m128 brod3 = _mm_set1_ps(b[2]);
__m128 brod4 = _mm_set1_ps(b[3]);
__m128 dot4 = _mm_add_ps(
_mm_add_ps(_mm_mul_ps(brod1, vx), _mm_mul_ps(brod2, vy)),
_mm_add_ps(_mm_mul_ps(brod3, vz), _mm_mul_ps(brod4, vw)));
_mm_store_ps(out, dot4);
}
int main() {
float *aosoa = (float*)_mm_malloc(sizeof(float)*16, 16);
/* initialize array to AoSoA vectors v1 =(0,1,2,3}, v2 = (4,5,6,7), v3 =(8,9,10,11), v4 =(12,13,14,15) */
float a[] = {
0,4,8,12,
1,5,9,13,
2,6,10,14,
3,7,11,15,
};
for (int i=0; i<16; i++) aosoa[i] = a[i];
float *out = (float*)_mm_malloc(sizeof(float)*4, 16);
float b[] = {1,1,1,1};
dot4x4(aosoa, b, out);
printf("%f %f %f %f\n", out[0], out[1], out[2], out[3]);
_mm_free(aosoa);
_mm_free(out);
}
答案 3 :(得分:1)
(事实上,尽管所有的选票都是在这个问题发布时给出的答案 没有达到我的期望。这是我等待的答案。)
SSE指令
shufps $IMM, xmmA, xmmB
不起作用
xmmB = f($IMM, xmmA)
//set xmmB with xmmA's words shuffled according to $IMM
但是作为
xmmB = f($IMM, xmmA, xmmB)
//set xmmB with 2 words of xmmA and 2 words of xmmB according to $IMM
这就是为什么需要从mulps
到xmm0
的{{1}}结果的副本。