c++ - vector 迭代编译成非常不同的指令

标签 c++ loops

令我惊讶的是,使用最新的编译器对 vector 进行迭代会针对相同的结果显示截然不同的代码。 这是代码:

void fun1(vector <uint8_t> &a,unsigned num) {

    for (auto &&value : a) {
        value += num;
    }
}

void fun2(vector <uint8_t> &a,unsigned num){

    for (uint32_t x = 0, p = a.size(); x < p; x++){
        a[x] += num;
    }
}
void fun4(vector <uint8_t> &a,unsigned num){

    for (uint32_t x = 0; x < a.size(); x++){
        a[x] += num;
    }
}
void fun3(vector <uint8_t> &a,unsigned num) {

    for (auto it = a.begin(); it!=a.end(); ++it) {
        *it += num;
    }
}
void fun5(vector <uint8_t> &a,unsigned num) {

    std::for_each(a.begin(), a.end(), [num](auto &&val) {
        val += num;
    });
}

您可以在 godbolt 上运行它.

生成的程序集非常不同(fun1 && fun5 甚至建议使用 SIMD)。我得到了 fun4 因为 a.size() 可能会改变。但其他人呢?不同的编译器近似于相同的结果。

编译器应该能够生成相同的代码,因为这里的意图非常基本(并且相同)。

我真的很喜欢fun2版本。有什么改进的方法吗?

更新:

当向编译器提供足够的信息时,这就不是问题了。因此它表明当前版本 (gcc 9.x) 中的编译器针对那些只有部分信息可访问的循环进行了不同的定制。

代码仍然不一样,但他们现在都提出了矢量化。我想我们将不得不接受它。 也许 future 的版本会改进这一点。

See the updated code (with useless allocations, but still)

最佳答案

  • 编译器现在针对乱序执行优化了代码。相同的 C++ 代码可以编译成不同的汇编代码。
  • 所以,尽量不要为优化留下任何空间:-)

这里有两个fun4的版本:

void fun4_a(vector <uint8_t> &a, uint8_t num) {
    size_t n = a.size();
f1: if (n <= 0) goto f2;
    a[--n] += num;
    goto f1;
f2: return;
}

void fun4_b(vector <uint8_t> &a, uint8_t num) {
    size_t n = a.size();
    while (n > 0) a[--n] += num;
}

您可以在 godbolt 上运行它.

编译器 x86_64 gcc 9.1 为 fun4_a 和 fun4_b 生成了相同的程序集,但仍然展开了一个“添加”循环:

fun4_a(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned char):
        mov     rdx, QWORD PTR [rdi]
        mov     rax, QWORD PTR [rdi+8]
        mov     ecx, esi
        sub     rax, rdx
        je      .L1
        sub     rax, 1
        add     BYTE PTR [rdx+rax], sil
        test    rax, rax
        je      .L1
.L3:
        mov     rdx, QWORD PTR [rdi]
        sub     rax, 1
        add     rdx, rax
        add     BYTE PTR [rdx], cl
        test    rax, rax
        jne     .L3
.L1:

虽然编译器 x86-64 icc 19.0.1 决定对 fun4_b 进行更多优化:

fun4_a(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned char):
        mov       rcx, QWORD PTR [8+rdi]                        #806.26
        mov       rdx, rcx                                      #806.26
        mov       rax, QWORD PTR [rdi]                          #806.52
        sub       rdx, rax                                      #806.26
        je        ..B1.6        # Prob 18%                      #10.14
        xor       eax, eax                                      #10.5
..B1.3:                         # Preds ..B1.4 ..B1.2
        inc       rax                                           #10.5
        mov       r8, rcx                                       #9.11
        lea       r9, QWORD PTR [rax+rax]                       #9.11
        sub       r8, r9                                        #9.11
        neg       r9                                            #9.11
        add       r9, rdx                                       #9.11
        mov       rdi, r9                                       #9.11
        add       BYTE PTR [1+r8], sil                          #11.3
        inc       rdi                                           #9.11
        je        ..B1.6        # Prob 18%                      #10.14
        add       BYTE PTR [r8], sil                            #11.3
        test      r9, r9                                        #10.14
        jne       ..B1.3        # Prob 82%                      #10.14
..B1.6:                         # Preds ..B1.3 ..B1.4 ..B1.1
        ret                                                     #13.5
fun4_b(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned char):
        mov       r8d, esi                                      #16.47
        mov       rsi, QWORD PTR [rdi]                          #806.52
        mov       rcx, QWORD PTR [8+rdi]                        #806.26
        sub       rcx, rsi                                      #806.26
        je        ..B2.17       # Prob 50%                      #18.16
        cmp       rcx, 16                                       #18.5
        jb        ..B2.18       # Prob 10%                      #18.5
        mov       rdx, rsi                                      #18.5
        and       rdx, 15                                       #18.5
        je        ..B2.9        # Prob 50%                      #18.5
        mov       rax, rdx                                      #18.5
        neg       rax                                           #18.5
        lea       rdx, QWORD PTR [16+rax]                       #18.5
        add       rax, 32                                       #18.5
        cmp       rcx, rax                                      #18.5
        jb        ..B2.18       # Prob 10%                      #18.5
        mov       rax, rcx                                      #18.5
        xor       r10d, r10d                                    #18.5
        sub       rax, rdx                                      #18.5
        mov       r9, rsi                                       #18.5
        and       rax, 15                                       #18.5
        neg       rax                                           #18.5
        add       rax, rcx                                      #18.5
        mov       edi, r8d                                      #18.20
..B2.7:                         # Preds ..B2.7 ..B2.6
        inc       r10                                           #18.5
        add       BYTE PTR [r9], dil                            #18.20
        inc       r9                                            #18.5
        cmp       r10, rdx                                      #18.5
        jb        ..B2.7        # Prob 82%                      #18.5
        jmp       ..B2.10       # Prob 100%                     #18.5
..B2.9:                         # Preds ..B2.3
        mov       rax, rcx                                      #18.5
        and       rax, 15                                       #18.5
        neg       rax                                           #18.5
        add       rax, rcx                                      #18.5
..B2.10:                        # Preds ..B2.7 ..B2.9
        movzx     edi, r8b                                      #18.29
        movd      xmm0, edi                                     #18.29
        punpcklbw xmm0, xmm0                                    #18.29
        punpcklwd xmm0, xmm0                                    #18.29
        punpckldq xmm0, xmm0                                    #18.29
        punpcklqdq xmm0, xmm0                                   #18.29
..B2.11:                        # Preds ..B2.11 ..B2.10
        movdqu    xmm1, XMMWORD PTR [rsi+rdx]                   #18.20
        paddb     xmm1, xmm0                                    #18.20
        movdqu    XMMWORD PTR [rdx+rsi], xmm1                   #18.20
        add       rdx, 16                                       #18.5
        cmp       rdx, rax                                      #18.5
        jb        ..B2.11       # Prob 82%                      #18.5
..B2.13:                        # Preds ..B2.11 ..B2.18
        add       rsi, rax                                      #18.5
        cmp       rax, rcx                                      #18.5
        jae       ..B2.17       # Prob 9%                       #18.5
..B2.15:                        # Preds ..B2.13 ..B2.15
        inc       rax                                           #18.5
        add       BYTE PTR [rsi], r8b                           #18.20
        inc       rsi                                           #18.5
        cmp       rax, rcx                                      #18.5
        jb        ..B2.15       # Prob 82%                      #18.5
..B2.17:                        # Preds ..B2.15 ..B2.1 ..B2.13
        ret                                                     #19.1
..B2.18:                        # Preds ..B2.2 ..B2.4
        xor       eax, eax                                      #18.5
        jmp       ..B2.13       # Prob 100%                     #18.5

关于c++ - vector 迭代编译成非常不同的指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56390818/

相关文章:

c++ - 从双指针在 oct 文件中创建 NDArray

C++ 使用声明和参数依赖查找

python - 计算迭代操作百分比的最佳方法是什么?

javascript - 按需更新多个 Highcharts 实例的最佳性能

c++ - C++ 中的位模式

c++ - SFML 2.3 代码块崩溃

c++ - 我可以在 C++ 中使用智能指针作为类成员吗?

R - 循环遍历列表并计算要存储在矩阵中的值

C while循环无限循环EOF不起作用

java - 如何在循环中创建 try block ?