C++编译优化这个地方坑有点多啊(后悔开这个坑了)
先放笔记吧,后面有时间再补这方面的东西

<>C++编译优化

* 编译优化,其实是编译时间,代码空间, 程序性能直接做权衡
* 汇编生成指令
* g++ -S test.cpp -O1 test.s void test(bool cond, double *a, double *b, double
*c, int len) { for (int i = 0;i < len; i++) if (cond) c[i] += a[i] * b[i]; else
c[i] -= a[i] * b[i]; }
<>O0优化(默认)
pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp
.cfi_def_cfa_register %rbp movb %dil, %al andb $1, %al movb %al, -1(%rbp) movq
%rsi, -16(%rbp) movq %rdx, -24(%rbp) movq %rcx, -32(%rbp) movl %r8d, -36(%rbp)
movl $0, -40(%rbp) LBB0_1: ## =>This Inner Loop Header: Depth=1 movl -40(%rbp),
%eax cmpl -36(%rbp), %eax jge LBB0_7 ## %bb.2: ## in Loop: Header=BB0_1 Depth=1
testb $1, -1(%rbp) je LBB0_4 ## %bb.3: ## in Loop: Header=BB0_1 Depth=1 movq
-16(%rbp), %rax movslq -40(%rbp), %rcx movsd (%rax,%rcx,8), %xmm0 ## xmm0 =
mem[0],zero movq -24(%rbp), %rax movslq -40(%rbp), %rcx mulsd (%rax,%rcx,8),
%xmm0 movq -32(%rbp), %rax movslq -40(%rbp), %rcx addsd (%rax,%rcx,8), %xmm0
movsd %xmm0, (%rax,%rcx,8) jmp LBB0_5 LBB0_4: ## in Loop: Header=BB0_1 Depth=1
movq -16(%rbp), %rax movslq -40(%rbp), %rcx movsd (%rax,%rcx,8), %xmm1 ## xmm1
= mem[0],zero movq -24(%rbp), %rax movslq -40(%rbp), %rcx mulsd (%rax,%rcx,8),
%xmm1 movq -32(%rbp), %rax movslq -40(%rbp), %rcx movsd (%rax,%rcx,8), %xmm0 ##
xmm0 = mem[0],zero subsd %xmm1, %xmm0 movsd %xmm0, (%rax,%rcx,8) LBB0_5: ## in
Loop: Header=BB0_1 Depth=1 jmp LBB0_6 LBB0_6: ## in Loop: Header=BB0_1 Depth=1
movl -40(%rbp), %eax addl $1, %eax ## ++i movl %eax, -40(%rbp) jmp LBB0_1
LBB0_7: popq %rbp retq .cfi_endproc
* 禁止绝大多数优化
* 最快的编译时间,最好的调试效果
* 特点
* 所有的变量都在内存中,会有大量的内存读写操作,运算结果才会放在寄存器上
* 编译器不会做任何优化,会尽可能的按照用户代码生成指令
* 调试方便,最大可能保证功能正确(在gdb调试中,可以直接向内存写值,但如果变量存放在寄存器上,就没有办法改变变量的值 )
<>O1优化
pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp
.cfi_def_cfa_register %rbp testl %r8d, %r8d jle LBB0_5 ## %bb.1: movl %r8d,
%r8d xorl %eax, %eax movapd LCPI0_0(%rip), %xmm0 ## xmm0 = [-0.0E+0,-0.0E+0]
jmp LBB0_2 .p2align 4, 0x90 LBB0_4: ## in Loop: Header=BB0_2 Depth=1 addsd
(%rcx,%rax,8), %xmm1 movsd %xmm1, (%rcx,%rax,8) addq $1, %rax ## ++i cmpq %rax,
%r8 je LBB0_5 LBB0_2: ## =>This Inner Loop Header: Depth=1 movsd (%rsi,%rax,8),
%xmm1 ## xmm1 = mem[0],zero mulsd (%rdx,%rax,8), %xmm1 testb %dil, %dil jne
LBB0_4 ## %bb.3: ## in Loop: Header=BB0_2 Depth=1 xorpd %xmm0, %xmm1 ##取反操作 jmp
LBB0_4 LBB0_5: popq %rbp retq
* 做些基础优化来降低codesize来提高运行性能
* 不会做影响编译时间比较大的优化
* 编译器会基于代码块优化,生成更加简洁的指令,但不会做复杂的跨代码块优化
* 优化点
* 循环变量i通过寄存器来计算,不再写内存
* 循环变量a[i]/b[i]/c[i]时的基址通过寄存器计算,而不是读取内存
* if (cond) 的逻辑不再是两大块代码,而是通过简单的取反
* 生成的代码紧凑性能比O0好
* 破坏了O0的调试能力,但是整体没有优化多少,所以比较鸡肋,clang默认是O1
<>O2优化
## %bb.0: pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp,
%rbp .cfi_def_cfa_register %rbp pushq %r14 pushq %rbx .cfi_offset %rbx, -32
.cfi_offset %r14, -24 testl %r8d, %r8d jle LBB0_21 ## %bb.1: movl %r8d, %r9d
cmpl $4, %r8d jae LBB0_3 ## %bb.2: xorl %r8d, %r8d jmp LBB0_10 LBB0_3: leaq
(%rcx,%r9,8), %r8 leaq (%rsi,%r9,8), %rax cmpq %rcx, %rax seta %r14b leaq
(%rdx,%r9,8), %rax cmpq %rsi, %r8 seta %bl cmpq %rcx, %rax seta %r11b cmpq
%rdx, %r8 seta %r10b xorl %r8d, %r8d testb %bl, %r14b jne LBB0_10 ## %bb.4:
andb %r10b, %r11b jne LBB0_10 ## %bb.5: movl %r9d, %r8d andl $-4, %r8d xorl
%eax, %eax movapd LCPI0_0(%rip), %xmm0 ## xmm0 = [-0.0E+0,-0.0E+0] jmp LBB0_6
.p2align 4, 0x90 LBB0_8: ## in Loop: Header=BB0_6 Depth=1 movupd (%rcx,%rax,8),
%xmm3 addpd %xmm1, %xmm3 movupd 16(%rcx,%rax,8), %xmm1 addpd %xmm2, %xmm1
movupd %xmm3, (%rcx,%rax,8) movupd %xmm1, 16(%rcx,%rax,8) addq $4, %rax cmpq
%rax, %r8 je LBB0_9 LBB0_6: ## =>This Inner Loop Header: Depth=1 movupd
(%rsi,%rax,8), %xmm2 movupd 16(%rsi,%rax,8), %xmm3 movupd (%rdx,%rax,8), %xmm1
mulpd %xmm2, %xmm1 movupd 16(%rdx,%rax,8), %xmm2 mulpd %xmm3, %xmm2 testb %dil,
%dil jne LBB0_8 ## %bb.7: ## in Loop: Header=BB0_6 Depth=1 xorpd %xmm0, %xmm1
xorpd %xmm0, %xmm2 jmp LBB0_8 LBB0_9: cmpq %r9, %r8 je LBB0_21 LBB0_10: movq
%r8, %rax notq %rax testb $1, %r9b je LBB0_14 ## %bb.11: movsd (%rsi,%r8,8),
%xmm0 ## xmm0 = mem[0],zero mulsd (%rdx,%r8,8), %xmm0 testb %dil, %dil jne
LBB0_13 ## %bb.12: xorpd LCPI0_0(%rip), %xmm0 LBB0_13: addsd (%rcx,%r8,8),
%xmm0 movsd %xmm0, (%rcx,%r8,8) orq $1, %r8 LBB0_14: addq %r9, %rax jne LBB0_15
LBB0_21: popq %rbx popq %r14 popq %rbp retq LBB0_15: movapd LCPI0_0(%rip),
%xmm0 ## xmm0 = [-0.0E+0,-0.0E+0] jmp LBB0_16 .p2align 4, 0x90 LBB0_20: ## in
Loop: Header=BB0_16 Depth=1 addsd 8(%rcx,%r8,8), %xmm1 movsd %xmm1,
8(%rcx,%r8,8) addq $2, %r8 cmpq %r8, %r9 je LBB0_21 LBB0_16: ## =>This Inner
Loop Header: Depth=1 movsd (%rsi,%r8,8), %xmm1 ## xmm1 = mem[0],zero mulsd
(%rdx,%r8,8), %xmm1 testb %dil, %dil jne LBB0_18 ## %bb.17: ## in Loop:
Header=BB0_16 Depth=1 xorpd %xmm0, %xmm1 LBB0_18: ## in Loop: Header=BB0_16
Depth=1 addsd (%rcx,%r8,8), %xmm1 movsd %xmm1, (%rcx,%r8,8) movsd
8(%rsi,%r8,8), %xmm1 ## xmm1 = mem[0],zero mulsd 8(%rdx,%r8,8), %xmm1 jne
LBB0_20 ## %bb.19: ## in Loop: Header=BB0_16 Depth=1 xorpd %xmm0, %xmm1 jmp
LBB0_20 .cfi_endproc
* O1所有的优化的基础上,做所有能支持的优化,不再考虑代码空间和运行之间的tradeoff
* 会大量增加代码运行空间和编译时间
* i++优化成i += 2 (具体优化需要根据编译器来判定)
* 把数组向量化 (把标量操作转化成矩阵操作,循环展开)
* 向量化是非常关键的优化,把两个double放在xmm寄存器中同时计算
* 循环展开让一次循环迭代同时执行两个向量操作,通过器件冗余并行实现
* Clang在O2时就启用向量化,gcc在O3才启用
<>-O2 + -march=icelake-client优化
pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp
.cfi_def_cfa_register %rbp pushq %rbx .cfi_offset %rbx, -24 testl %r8d, %r8d
jle LBB0_16 ## %bb.1: movl %r8d, %r9d cmpl $16, %r8d jae LBB0_3 ## %bb.2: xorl
%eax, %eax jmp LBB0_10 LBB0_3: leaq (%rcx,%r9,8), %rax leaq (%rsi,%r9,8), %r8
leaq (%rdx,%r9,8), %r10 cmpq %rcx, %r8 seta %r11b cmpq %rsi, %rax seta %bl cmpq
%rcx, %r10 seta %r8b cmpq %rdx, %rax seta %r10b xorl %eax, %eax testb %bl,
%r11b jne LBB0_10 ## %bb.4: andb %r10b, %r8b jne LBB0_10 ## %bb.5: movl %r9d,
%eax andl $-16, %eax xorl %r8d, %r8d vbroadcastsd LCPI0_0(%rip), %ymm0 ## ymm0
= [-0.0E+0,-0.0E+0,-0.0E+0,-0.0E+0] jmp LBB0_6 .p2align 4, 0x90 LBB0_8: ## in
Loop: Header=BB0_6 Depth=1 vaddpd (%rcx,%r8,8), %ymm1, %ymm1 vaddpd
32(%rcx,%r8,8), %ymm2, %ymm2 vaddpd 64(%rcx,%r8,8), %ymm3, %ymm3 vaddpd
96(%rcx,%r8,8), %ymm4, %ymm4 vmovupd %ymm1, (%rcx,%r8,8) vmovupd %ymm2,
32(%rcx,%r8,8) vmovupd %ymm3, 64(%rcx,%r8,8) vmovupd %ymm4, 96(%rcx,%r8,8) addq
$16, %r8 cmpq %r8, %rax je LBB0_9 LBB0_6: ## =>This Inner Loop Header: Depth=1
vmovupd (%rsi,%r8,8), %ymm1 vmovupd 32(%rsi,%r8,8), %ymm2 vmovupd
64(%rsi,%r8,8), %ymm3 vmovupd 96(%rsi,%r8,8), %ymm4 vmulpd (%rdx,%r8,8), %ymm1,
%ymm1 vmulpd 32(%rdx,%r8,8), %ymm2, %ymm2 vmulpd 64(%rdx,%r8,8), %ymm3, %ymm3
vmulpd 96(%rdx,%r8,8), %ymm4, %ymm4 testb %dil, %dil jne LBB0_8 ## %bb.7: ## in
Loop: Header=BB0_6 Depth=1 vxorpd %ymm0, %ymm1, %ymm1 vxorpd %ymm0, %ymm2,
%ymm2 vxorpd %ymm0, %ymm3, %ymm3 vxorpd %ymm0, %ymm4, %ymm4 jmp LBB0_8 LBB0_9:
cmpq %r9, %rax je LBB0_16 LBB0_10: movq %rax, %r8 notq %r8 addq %r9, %r8 movq
%r9, %r10 andq $3, %r10 je LBB0_13 ## %bb.11: vmovapd LCPI0_1(%rip), %xmm0 ##
xmm0 = [-0.0E+0,-0.0E+0] .p2align 4, 0x90 LBB0_12: ## =>This Inner Loop Header:
Depth=1 vmovsd (%rsi,%rax,8), %xmm1 ## xmm1 = mem[0],zero vmulsd (%rdx,%rax,8),
%xmm1, %xmm1 vxorpd %xmm0, %xmm1, %xmm2 kmovd %edi, %k1 vmovsd %xmm1, %xmm2,
%xmm2 {%k1} vaddsd (%rcx,%rax,8), %xmm2, %xmm1 vmovsd %xmm1, (%rcx,%rax,8) incq
%rax decq %r10 jne LBB0_12 LBB0_13: cmpq $3, %r8 jb LBB0_16 ## %bb.14: vmovapd
LCPI0_1(%rip), %xmm0 ## xmm0 = [-0.0E+0,-0.0E+0] .p2align 4, 0x90 LBB0_15: ##
=>This Inner Loop Header: Depth=1 vmovsd (%rsi,%rax,8), %xmm1 ## xmm1 =
mem[0],zero vmulsd (%rdx,%rax,8), %xmm1, %xmm1 vxorpd %xmm0, %xmm1, %xmm2 kmovd
%edi, %k1 vmovsd %xmm1, %xmm2, %xmm2 {%k1} vaddsd (%rcx,%rax,8), %xmm2, %xmm1
vmovsd %xmm1, (%rcx,%rax,8) vmovsd 8(%rsi,%rax,8), %xmm1 ## xmm1 = mem[0],zero
vmulsd 8(%rdx,%rax,8), %xmm1, %xmm1 vxorpd %xmm0, %xmm1, %xmm2 vmovsd %xmm1,
%xmm2, %xmm2 {%k1} vaddsd 8(%rcx,%rax,8), %xmm2, %xmm1 vmovsd %xmm1,
8(%rcx,%rax,8) vmovsd 16(%rsi,%rax,8), %xmm1 ## xmm1 = mem[0],zero vmulsd
16(%rdx,%rax,8), %xmm1, %xmm1 vxorpd %xmm0, %xmm1, %xmm2 vmovsd %xmm1, %xmm2,
%xmm2 {%k1} vaddsd 16(%rcx,%rax,8), %xmm2, %xmm1 vmovsd %xmm1, 16(%rcx,%rax,8)
vmovsd 24(%rsi,%rax,8), %xmm1 ## xmm1 = mem[0],zero vmulsd 24(%rdx,%rax,8),
%xmm1, %xmm1 vxorpd %xmm0, %xmm1, %xmm2 vmovsd %xmm1, %xmm2, %xmm2 {%k1} vaddsd
24(%rcx,%rax,8), %xmm2, %xmm1 vmovsd %xmm1, 24(%rcx,%rax,8) addq $4, %rax cmpq
%rax, %r9 jne LBB0_15 LBB0_16: popq %rbx popq %rbp vzeroupper retq .cfi_endproc
*
march

* 为特定的cpu类型生成指令
* 生成的指令不一定能在其他平台上运行
* -march默认enable-mtune
*
mtune

* 为特定的cpu类型优化代码生成(一般是做调度方面的优化)
*
Icelake cpu拥有256位寄存器,可以同时计算4个double,同时支持四个运算单元同时计算,进一步优化i++的效率

<>向量化条件

* 向量化还需要一个额外条件, a a a与 b b b c c c数组的内存是不能 a l i a s alias alias
* 同时,向量化还需要scalar的版本来处理remainder部分
* a l i a s alias alias分析非常占用编译时间
* a l i a s alias alias分析是一个图的 n p np np问题
* 所有编译优化的基础都是 a l i a s alias alias分析
<>O3优化

* O2所有的优化基础上,牺牲编译时间来极尽可能来优化性能
* 主要是跨BB的loop变化,数据流分析等
* 由于O3会做很多激进的分析,大规模变换源代码,更容易暴露源代码逻辑的问题
* 代码逻辑从先循环再判断改为先判断再循环,判断走哪一个指令只需要判断一次

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信