7

我从以下 SIMD 代码中检查了 Visual Studio 2012 的程序集输出:

    float *end = arr + sz;
    float *b = other.arr;
    for (float *a = arr; a < end; a += 4, b += 4)
    {
        __m128 ax = _mm_load_ps(a);
        __m128 bx = _mm_load_ps(b);
        ax = _mm_add_ps(ax, bx);
        _mm_store_ps(a, ax);
    }

循环体是:

$LL11@main:
    movaps  xmm1, XMMWORD PTR [eax+ecx]
    addps   xmm1, XMMWORD PTR [ecx]
    add ecx, 16                 ; 00000010H
    movaps  XMMWORD PTR [ecx-16], xmm1
    cmp ecx, edx
    jb  SHORT $LL11@main

为什么要增加ecx16,只在存储下一行时减去 16?

4

3 回答 3

7

好吧,这里基本上有两种选择。

 add ecx, 16
 movaps XMMWORD PTR [ecx-16], xmm1 ; stall for ecx?
 cmp ecx, edx
 jb loop

或者

 movaps XMMWORD PTR [ecx], xmm1
 add ecx, 16
 cmp ecx, edx ; stall for ecx?
 jb loop

在选项 1 中,您可能会在add和之间出现停顿movapsadd在选项 2 中,您在和之间有潜在的停顿cmp。但是,也存在使用的执行单元的问题。addcmp(= sub) 使用 ALU,而[ecx-16]使用 AGU(地址生成单元),我相信。所以我怀疑选项 1 可能会略有胜利,因为 ALU 的使用与 AGU 的使用交错。

于 2013-09-11T11:06:40.737 回答
4

ADDPS 有 3 个周期的延迟,加上内存负载,因此下一个更快的 ADD 将在下一个 MOVAPS 之前完成,该 MOVAPS 需要 xmm1 寄存器中 ADDPS 的结果才能开始。

于 2013-09-11T12:25:16.473 回答
1

的确,这有点奇怪。

许多编译器在指令修改后避免读取指令中的寄存器,因为这样的代码在某些处理器上运行速度较慢。例子:

; Code that runs fast:
add ecx, 16
mov esi, edi
cmp ecx, edx

; Code doing the same that may run slower:
mov esi, edi
add ecx, 16
cmp ecx, edx

由于这个原因,编译器经常改变汇编指令的顺序。但是,在您的情况下,这绝对不是原因。

也许编译器的优化代码没有 100% 正确编写,因此它会进行这种“优化”。

于 2013-09-11T04:50:42.637 回答