5

我发现在一些用于进行数学计算的 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 版本,结果大约是 50 毫秒。

对于movlps,movhps版本,结果大约是46ms。

而且我还尝试了一个数据对齐版本,在结构上使用 __declspec(align(16)) 描述符,并由 _aligned_malloc() 分配,结果大约是 34 毫秒。

为什么 movlps 和 movhps 的组合更快?这是否意味着我们最好使用 movlps 和 movhps 而不是 movups?

4

2 回答 2

6

这一代的 Athlon (K8) 只有 64 位宽的 ALU 单元。因此,每条 128 位 SSE 指令都需要拆分为两条 64 位指令,这会导致某些指令的开销。

在这种类型的处理器上,与相同的 MMX 代码相比,您通常会发现使用 SSE 没有加速。

在 Intel、AMD 和 VIA CPU 的微架构中引用 Agner Fog :汇编程序员和编译器制造商的优化指南:

12.9 64 位与 128 位指令

在 K10 上使用 128 位指令是一个很大的优势,但在 K8 上则不然,因为每条 128 位指令在 K8 上被分成两个 64 位宏操作。

128 位存储器写入指令在 K10 上作为两个 64 位宏操作处理,而 128 位存储器读取是通过 K10 上的单个宏操作(K8 上的 2 个)完成的。

128 位内存读取指令仅使用 K8 上的 FMISC 单元,但使用 K10 上的所有三个单元。因此,在 k8 上使用 XMM 寄存器将数据块从一个内存位置移动到另一个内存位置是不利的,但在 K10 上是有利的。

于 2012-11-23T08:33:21.560 回答
1

movups 适用于非对齐数据。movlps, movhps 仅适用于对齐数据。当然movlps,movhps更快。对于时间计算和比较,最好使用 rdtsc,而不是 ms。

于 2012-11-23T08:12:59.887 回答