Различные сборки при ранжировании простого алгоритма - PullRequest
6 голосов
/ 16 мая 2019

Когда я готовил дополнительную информацию для этого вопроса , я заметил, что «ранжированные» реализации очень простого алгоритма привели к важным (на мой взгляд) различиям в результирующей сборке по сравнению с «устаревшим» реализации.

Я немного расширил тесты со следующими результатами (GCC 9.1 -O3):

  • Случай 1. Простой для цикла (https://godbolt.org/z/rAVaT2)

     #include <vector>
    
     void foo(std::vector<double> &u, std::vector<double> const &v)
     {
       for (std::size_t i = 0u; i < u.size(); ++i)
         u[i] += v[i];
     }
    
        mov rdx, QWORD PTR [rdi]
        mov rdi, QWORD PTR [rdi+8]
        sub rdi, rdx
        sar rdi, 3
        je .L1
        mov rcx, QWORD PTR [rsi]
        lea rax, [rcx+15]
        sub rax, rdx
        cmp rax, 30
        jbe .L7
        lea rax, [rdi-1]
        cmp rax, 1
        jbe .L7
        mov rsi, rdi
        xor eax, eax
        shr rsi
        sal rsi, 4
        .L4:
        movupd xmm0, XMMWORD PTR [rcx+rax]
        movupd xmm1, XMMWORD PTR [rdx+rax]
        addpd xmm0, xmm1
        movups XMMWORD PTR [rdx+rax], xmm0
        add rax, 16
        cmp rsi, rax
        jne .L4
        mov rsi, rdi
        and rsi, -2
        and edi, 1
        je .L1
        lea rax, [rdx+rsi*8]
        movsd xmm0, QWORD PTR [rax]
        addsd xmm0, QWORD PTR [rcx+rsi*8]
        movsd QWORD PTR [rax], xmm0
        ret
        .L7:
        xor eax, eax
        .L3:
        movsd xmm0, QWORD PTR [rdx+rax*8]
        addsd xmm0, QWORD PTR [rcx+rax*8]
        movsd QWORD PTR [rdx+rax*8], xmm0
        add rax, 1
        cmp rdi, rax
        jne .L3
        .L1:
        ret
  • Дело 2. std::transform (https://godbolt.org/z/2iZaqo)

    #include <algorithm>
    #include <vector>
    
    void foo(std::vector<double> &u, std::vector<double> const &v)
    {
    std::transform(std::begin(u), std::end(u),
                   std::begin(v),
                   std::begin(u),
                   std::plus());
    }
    
        mov rdx, QWORD PTR [rdi]
        mov rax, QWORD PTR [rdi+8]
        mov rsi, QWORD PTR [rsi]
        cmp rax, rdx
        je .L1
        sub rax, 8
        lea rcx, [rsi+15]
        sub rax, rdx
        sub rcx, rdx
        shr rax, 3
        cmp rcx, 30
        jbe .L7
        movabs rcx, 2305843009213693950
        test rax, rcx
        je .L7
        lea rcx, [rax+1]
        xor eax, eax
        mov rdi, rcx
        shr rdi
        sal rdi, 4
        .L4:
        movupd xmm0, XMMWORD PTR [rdx+rax]
        movupd xmm1, XMMWORD PTR [rsi+rax]
        addpd xmm0, xmm1
        movups XMMWORD PTR [rdx+rax], xmm0
        add rax, 16
        cmp rax, rdi
        jne .L4
        mov rdi, rcx
        and rdi, -2
        lea rax, [0+rdi*8]
        add rdx, rax
        add rsi, rax
        cmp rcx, rdi
        je .L1
        movsd xmm0, QWORD PTR [rdx]
        addsd xmm0, QWORD PTR [rsi]
        movsd QWORD PTR [rdx], xmm0
        ret
        .L7:
        xor ecx, ecx
        .L3:
        movsd xmm0, QWORD PTR [rdx+rcx*8]
        addsd xmm0, QWORD PTR [rsi+rcx*8]
        mov rdi, rcx
        movsd QWORD PTR [rdx+rcx*8], xmm0
        add rcx, 1
        cmp rax, rdi
        jne .L3
        .L1:
        ret
  • Случай 3. Range-v3 view::zip (https://godbolt.org/z/0BEkfT)

    #define RANGES_ASSERT(...) ((void)0)
    
    #include <algorithm>
    #include <range/v3/view/zip.hpp>
    #include <vector>
    
    void foo(std::vector<double> &u, std::vector<double> const &v)
    {
    auto w = ranges::view::zip(u, v);
    
    std::for_each(std::begin(w), std::end(w),
                  [](auto &&x) { std::get<0u>(x) += std::get<1u>(x); });
    }
    
        mov rdx, QWORD PTR [rsi]
        mov rsi, QWORD PTR [rsi+8]
        mov rax, QWORD PTR [rdi]
        mov rcx, QWORD PTR [rdi+8]
        cmp rdx, rsi
        je .L1
        cmp rax, rcx
        je .L1
        .L3:
        movsd xmm0, QWORD PTR [rax]
        addsd xmm0, QWORD PTR [rdx]
        add rax, 8
        add rdx, 8
        movsd QWORD PTR [rax-8], xmm0
        cmp rax, rcx
        je .L1
        cmp rdx, rsi
        jne .L3
        .L1:
        ret
  • Дело 4. cmcstl2 ranges::transform (https://godbolt.org/z/MjYO1G)

    #include <experimental/ranges/algorithm>
    #include <vector>
    
    namespace std
    {
    namespace ranges = experimental::ranges;
    }
    
    void foo(std::vector<double> &u,s td::vector<double> const &v)
    {
    std::ranges::transform(std::ranges::begin(u), std::ranges::end(u),
                           std::ranges::begin(v), std::ranges::end(v),
                           std::ranges::begin(u),
                           std::plus());
    }
    
        mov r8, QWORD PTR [rsi+8]
        mov rdx, QWORD PTR [rsi]
        mov rax, QWORD PTR [rdi]
        mov rcx, QWORD PTR [rdi+8]
        cmp rdx, r8
        je .L1
        cmp rcx, rax
        jne .L3
        jmp .L1
        .L16:
        cmp rdx, r8
        je .L1
        .L3:
        movsd xmm0, QWORD PTR [rax]
        addsd xmm0, QWORD PTR [rdx]
        add rax, 8
        add rdx, 8
        movsd QWORD PTR [rax-8], xmm0
        cmp rax, rcx
        jne .L16
        .L1:
        ret

Я не могу прочитать сборку, но мне кажется, что я понимаю, что сборки в случае 1 и 2 практически эквивалентны и включают в себя упакованные суммы, в то время как сборка версий диапазонов (случаи 3 и 4) гораздо более краткая, но не векторизовано.

Мне бы очень хотелось понять, что означают эти различия. Есть ли смысл в моей интерпретации сборки? Каковы дополнительные инструкции в версиях без диапазона? Почему есть эти различия?

...