我发现在一些用于进行数学计算的SSE优化代码中,他们使用movlps和movhps指令的组合而不是单个movups指令来传输未对齐的数据。我不知道为什么,所以我自己尝试了,这是下面的伪代码:
struct Vec4
{
float f[4];
};
const size_t nSize = sizeof(Vec4) * 100;
Vec4* pA = (Vec4*)malloc( nSize );
Vec4* pB = (Vec4*)malloc( nSize );
Vec4* pR = (Vec4*)malloc( nSize );
...Some data initialization code here
...Records current time by QueryPerformanceCounter()
for( int i=0; i<100000, ++i )
{
for( int j=0; j<100; ++j )
{
Vec4* a = &pA[i];
Vec4* b = &pB[i];
Vec4* r = &pR[i];
__asm
{
mov eax, a
mov ecx, b
mov edx, r
...option 1:
movups xmm0, [eax]
movups xmm1, [ecx]
mulps xmm0, xmm1
movups [edx], xmm0
...option 2:
movlps xmm0, [eax]
movhps xmm0, [eax+8]
movlps xmm1, [ecx]
movhps xmm1, [ecx+8]
mulps xmm0, xmm1
movlps [edx], xmm0
movhps [edx+8], xmm0
}
}
}
...Calculates passed time
free( pA );
free( pB );
free( pR );
我多次运行代码并计算平均耗时。
对于movups版本,结果大约是50ms。
对于movlps,movhps版本,结果大约是46ms。
我还在结构上尝试了带有__declspec(align(16))描述符的数据对齐版本,并由_aligned_malloc()分配,结果大约是34ms。
为什么movlps和movhps的组合更快?这是否意味着我们最好使用movlps和movhps而不是movups?
答案 0 :(得分:5)
这一代(K8)的Athlons只有64位宽的ALU单元。因此,每个128位SSE指令需要分成两个64位指令,这会产生一些指令的开销。
在这种类型的处理器上,与相同的MMX代码相比,您通常不会发现使用SSE加速。
中引用Agner Fog12.9 64位与128位指令
在K10上使用128位指令是一个很大的优势,但在K8上不是因为每个128位 指令在K8上被分成两个64位宏操作。
128位存储器写指令在K10上作为两个64位宏操作处理,而 通过K10上的单个宏操作完成128位存储器读取(K8上为2)。
128位存储器读取指令仅使用K8上的FMISC单元,而是使用K10上的所有三个单元。 因此,仅使用XMM寄存器来移动数据块是不利的 在k8上有一个记忆位置到另一个,但在K10上是有利的。
答案 1 :(得分:1)
movups适用于非对齐数据。 movlps,movhps仅适用于所有数据。肯定是movlps,movhps更快。对于时间计算和比较更好地使用rdtsc,而不是ms。