gpt4 book ai didi

c++ - std::vector sum ASM 解释

转载 作者:太空狗 更新时间:2023-10-29 20:11:14 27 4
gpt4 key购买 nike

我正在玩弄 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,%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

为了比较,具有相同功能但使用 std::vector<double> 的 ASM是:

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> 正在进行一些巧妙的优化,但我有点不知所措,无法解释发生了什么。有没有大佬解惑一下?

最佳答案

简短的回答 - 编译器已经向量化并展开了用于添加整数的循环。比较 vector<double>具有这些行的版本:

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

这意味着它向总和中添加一个 double 值,然后继续移动 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显示它一次执行 16 个字节(4 个整数)和 subq $-128, %rsi显示它在 8 个负载的单个循环中执行 128 个字节(或 32 个整数)。循环每次迭代的最终结果将接下来的 32 个整数添加到 xmm0:xmm1 中的 8 个槽之一

LBB0_11然后从主循环中获取输出(xmm0 和 xmm1 中的 8 个整数)并求出它们的总和。

LBB0_12然后完成主循环无法使用的 vector 末尾的任何整数(因为主循环同时处理 32 个整数)

它将加法矢量化,因此它可以一次处理 4 个整数,这通常比一次处理一个整数要快。它还展开循环,以便它可以在每个循环中执行超过 1 次的添加迭代。

矢量化说明:What does vectorization mean?

循环展开的解释:When, if ever, is loop unrolling still useful?

我没有分析整数情况下代码的开头,但通常这是通过在开始主循环之前将其对齐到 16 字节边界来设置循环。

关于c++ - std::vector<int> sum ASM 解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34265493/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com