为什么Visual Studio在解除引用之前递增循环指针?

时间:2013-09-11 04:31:11

标签: c++ visual-studio-2012 assembly code-generation pipelining

我从以下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

为什么将ecx增加16,只有在向下一行存储时减去16?

3 个答案:

答案 0 :(得分: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中,您可能会在addmovaps之间停顿。在选项2中,您可能会在addcmp之间停顿。但是,还存在使用执行单元的问题。我相信,addcmp(= sub)使用ALU,[ecx-16]使用AGU(地址生成单位)。因此我怀疑选项1中可能会略微获胜,因为ALU的使用与AGU的使用是交错的。

答案 1 :(得分:4)

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

答案 2 :(得分: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%正确编写,因此它会进行这种“优化”。