我正在玩Compiler Explorer,我很难理解简单std::vector
求和函数的ASM输出(x86 Clang 3.7 -O3):
#include#include int sum(const std::vector & v) { return std::accumulate(v.begin(), v.end(), 0); }
此代码的ASM是:
sum(std::vector> const&): # @sum(std::vector > 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>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, int) [clone .exit] req
为了比较,使用相同功能的ASM std::vector
是:
sum(std::vector> 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
ASM std::vector
似乎相当微不足道,而ASM std::vector
似乎显得更加复杂.我假设有一些聪明的优化正在进行std::vector
,但我有点不知道解释发生了什么.有人可以开导我吗?
简短的回答 - 编译器已经向量化并展开了用于添加整数的循环.比较vector
具有以下行的版本:
addsd (%rax), %xmm0 addq $8, %rax
这意味着它在总和中添加一个double,然后在8个字节上移动并循环.
vector
版本主循环中的相同代码可以:
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
显示其在跨越8个负载单个环路做128个字节(或32个整数).循环的每次迭代的最终结果将接下来的32个整数添加到xmm0:xmm1中的8个插槽之一
LBB0_11
然后获取主循环的输出(跨越xmm0和xmm1的8个整数)并找到它们的总和.
LBB0_12
然后在向量的末尾完成任何不能被主循环消耗的整数(因为主循环同时在32个整数上工作)
它会对添加进行矢量化,因此它可以同时处理4个整数,这通常比一次执行一个整数更快.它还会展开循环,以便每循环添加多次迭代.
矢量化的解释:矢量化是什么意思?
循环展开的解释:如果有的话,循环展开仍然有用吗?
我没有分析整数情况的代码的开始,但通常这是通过在启动主循环之前将其对齐到16字节边界来设置循环.