2015-12-14 39 views
3

我被玩弄的Compiler Explorer,而我努力理解一個簡單的std::vector<int>和函數的ASM輸出(86鏘3.7 -O3):的std ::矢量<int>和ASM的說明

#include <vector> 
#include <numeric> 

int sum(const std::vector<int>& v) 
{ 
    return std::accumulate(v.begin(), v.end(), 0); 
} 

此代碼的ASM是:

sum(std::vector<int, std::allocator<int> > const&):    # @sum(std::vector<int, std::allocator<int> > const&) 
     movq (%rdi), %rsi 
     movq 8(%rdi), %r11 
     xorl %eax, %eax 
     cmpq %r11, %rsi 
     je  .LBB0_13 
     movabsq $9223372036854775800, %rax # imm = 0x7FFFFFFFFFFFFFF8 
     leaq -4(%r11), %rdx 
     movq %rdx, %r10 
     subq %rsi, %r10 
     shrq $2, %r10 
     incq %r10 
     xorl %edi, %edi 
     movq %r10, %r8 
     andq %rax, %r8 
     pxor %xmm0, %xmm0 
     je  .LBB0_2 
     andq %r10, %rax 
     leaq -8(%rax), %r9 
     movl %r9d, %ecx 
     shrl $3, %ecx 
     incl %ecx 
     xorl %edi, %edi 
     testb $3, %cl 
     je  .LBB0_4 
     subl %esi, %edx 
     shrl $2, %edx 
     incl %edx 
     andl $24, %edx 
     addl $-8, %edx 
     shrl $3, %edx 
     incl %edx 
     andl $3, %edx 
     negq %rdx 
     pxor %xmm0, %xmm0 
     xorl %edi, %edi 
     pxor %xmm1, %xmm1 
.LBB0_6:        # %vector.body.prol 
     movdqu (%rsi,%rdi,4), %xmm2 
     movdqu 16(%rsi,%rdi,4), %xmm3 
     paddd %xmm2, %xmm0 
     paddd %xmm3, %xmm1 
     addq $8, %rdi 
     incq %rdx 
     jne  .LBB0_6 
     jmp  .LBB0_7 
.LBB0_2: 
     pxor %xmm1, %xmm1 
     jmp  .LBB0_11 
.LBB0_4: 
     pxor %xmm0, %xmm0 
     pxor %xmm1, %xmm1 
.LBB0_7:        # %vector.body.preheader.split 
     leaq (%rsi,%r8,4), %rdx 
     cmpq $24, %r9 
     jb  .LBB0_10 
     subq %rdi, %rax 
     leaq 112(%rsi,%rdi,4), %rsi 
.LBB0_9:        # %vector.body 
     movdqu -112(%rsi), %xmm2 
     movdqu -96(%rsi), %xmm3 
     movdqu -80(%rsi), %xmm4 
     movdqu -64(%rsi), %xmm5 
     paddd %xmm0, %xmm2 
     paddd %xmm1, %xmm3 
     paddd %xmm4, %xmm2 
     paddd %xmm5, %xmm3 
     movdqu -48(%rsi), %xmm4 
     movdqu -32(%rsi), %xmm5 
     paddd %xmm2, %xmm4 
     paddd %xmm3, %xmm5 
     movdqu -16(%rsi), %xmm0 
     movdqu (%rsi), %xmm1 
     paddd %xmm4, %xmm0 
     paddd %xmm5, %xmm1 
     subq $-128, %rsi 
     addq $-32, %rax 
     jne  .LBB0_9 
.LBB0_10: 
     movq %rdx, %rsi 
     movq %r8, %rdi 
.LBB0_11:        # %middle.block 
     paddd %xmm1, %xmm0 
     pshufd $78, %xmm0, %xmm1  # xmm1 = xmm0[2,3,0,1] 
     paddd %xmm0, %xmm1 
     pshufd $229, %xmm1, %xmm0  # xmm0 = xmm1[1,1,2,3] 
     paddd %xmm1, %xmm0 
     movd %xmm0, %eax 
     cmpq %rdi, %r10 
     je  .LBB0_13 
.LBB0_12:        # %.lr.ph.i 
     addl (%rsi), %eax 
     addq $4, %rsi 
     cmpq %rsi, %r11 
     jne  .LBB0_12 
.LBB0_13:        # %int std::accumulate<__gnu_cxx::__normal_iterator<int const*, std::vector<int, std::allocator<int> > >, int>(__gnu_cxx::__normal_iterator<int const*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int const*, std::vector<int, std::allocator<int> > >, int) [clone .exit] 
     req 

爲了比較,ASM對於相同的功能,但是使用std::vector<double>是:

sum(std::vector<double, std::allocator<double> > const&): 
     movq 8(%rdi), %rdx 
     movq (%rdi), %rax 
     pxor %xmm0, %xmm0 
     cmpq %rax, %rdx 
     je  .L4 
.L3: 
     addsd (%rax), %xmm0 
     addq $8, %rax 
     cmpq %rax, %rdx 
     jne  .L3 
     rep ret 
.L4: 
     rep ret 

std::vector<double>的ASM看起來相當平凡,而std::vector<int>的ASM看起來明顯更復雜。我假設有一些巧妙的優化與std::vector<int>進行,但我有點虧損解釋發生了什麼事情。有人能啓發我嗎?

+0

你可以看到'-fno-vectorize'和'-fno-unroll-loops'的區別。 –

+0

你會得到一個更有趣的向量化('addpd',而不是'addsd')版本,'-ffast-math',還有'-mavx'允許256b個向量。 –

回答

7

簡答 - 編譯器已經矢量化並展開了用於添加整數的循環。比較vector<double>版本,其具有這些行:

addsd (%rax), %xmm0 
addq $8, %rax 

這意味着其添加單個雙入總和,然後移動上8個字節和循環。

在的vector<int>版本的主循環的同一代碼的功能:

movdqu -112(%rsi), %xmm2 
movdqu -96(%rsi), %xmm3 
movdqu -80(%rsi), %xmm4 
movdqu -64(%rsi), %xmm5 
... 
movdqu -48(%rsi), %xmm4 
movdqu -32(%rsi), %xmm5 
... 
movdqu -16(%rsi), %xmm0 
... 
movdqu (%rsi), %xmm1 
... 
subq $-128, %rsi 

movdq顯示其在一次(4個整數)做16個字節和subq $-128, %rsi示出其操作的方式爲128字節(或32個整數)在8個負載的單個循環中。循環的每次迭代的最終結果將接下來的32個int添加到xmm0中的8個槽中的一箇中:xmm1

LBB0_11然後從主循環(跨越xmm0和xmm1的8個整數)獲取輸出,這些的總和。

LBB0_12然後結束關閉在其不能通過主循環消耗的矢量的末端的任何整數(如主迴路適用於在同一時間32點的整數)

它vectorises的增加,因此可以一次處理4個整數,一般比每次執行一個整數要快。它還展開循環,以便每循環添加多次迭代。

量化的說明:What does vectorization mean?

循環展開的說明:When, if ever, is loop unrolling still useful?

我還沒有分析整數情況下,代碼的開始,但通常這是由它對準建立循環一個16字節的邊界,然後開始主循環。

+2

請注意,使用編譯器選項'-fast-math'的'std :: vector '會發生類似的情況。 –

相關問題