c – std :: vector sum ASM的解释
我正在玩
Compiler Explorer,我正在努力理解一个简单的std :: vector< int>的ASM输出(x86 Clang 3.7 -O3).和函数:
#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,%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,%rdx cmpq $24,%r9 jb .LBB0_10 subq %rdi,%rax leaq 112(%rsi,%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,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*,__gnu_cxx::__normal_iterator<int const*,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>,但我有点亏本来解释发生了什么.有人可以开导我吗? 解决方法
简短回答 – 编译器已经向量化并展开了用于添加整数的循环.比较矢量< double>有这些行的版本:
addsd (%rax),%xmm0 addq $8,%rax 这意味着它在总和中添加一个double,然后在8个字节上移动并循环. 向量< 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显示它一次执行16个字节(4个整数)和subq $-128,%rsi显示它在8个负载的单个循环中执行128个字节(或32个int).循环的每次迭代的最终结果将接下来的32个整数添加到xmm0:xmm1中的8个插槽之一 LBB0_11然后获取主循环的输出(跨xmm0和xmm1为8个整数)并找到它们的总和. 然后LBB0_12完成向量末尾的任何整数,主循环无法使用(因为主循环同时在32个整数上运行) 它会对添加进行矢量化,因此它可以同时处理4个整数,这通常比一次执行一个整数更快.它还会展开循环,以便每循环添加多次迭代. 矢量化说明:What does vectorization mean? 循环展开的说明:When,if ever,is loop unrolling still useful? 我没有分析整数情况的代码的开始,但通常这是通过在启动主循环之前将其对齐到16字节边界来设置循环. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |