【CMAKE】c++代码编译加速以及优化项

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.htmlhttps://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

一般来说,如果不指定优化标识的话,gcc就会产生可调试代码,每条指令之间将是独立的:可以在指令之间设置断点,使用gdb中的 p命令查看变量的值,改变变量的值等。并且优先采用最快的编译速度

当优化标识被启用之后,gcc编译器将会改变程序的结构(当然会在保证变换之后的程序与源程序语义等价的前提之下),以满足代码大小最小或运行速度更快,当然二者是不可兼得的。在不同的gcc配置和目标平台下,同一个标识所采用的优化种类也是不一样的。-O会在大大优化循环中的运行速度。

优化参数

-O1

这两个命令的效果是一样的,目的都是在不影响编译速度的前提下,尽量采用一些优化算法降低代码大小和可执行代码的运行速度。并开启如下的优化选项:

-fauto-inc-dec 
-fbranch-count-reg 
-fcombine-stack-adjustments 
-fcompare-elim 
-fcprop-registers 
-fdce 
-fdefer-pop 
-fdelayed-branch 
-fdse 
-fforward-propagate 
-fguess-branch-probability 
-fif-conversion2 
-fif-conversion 
-finline-functions-called-once 
-fipa-pure-const 
-fipa-profile 
-fipa-reference 
-fmerge-constants 
-fmove-loop-invariants 
-freorder-blocks 
-fshrink-wrap 
-fshrink-wrap-separate 
-fsplit-wide-types 
-fssa-backprop 
-fssa-phiopt 
-fstore-merging 
-ftree-bit-ccp 
-ftree-ccp 
-ftree-ch 
-ftree-coalesce-vars 
-ftree-copy-prop 
-ftree-dce 
-ftree-dominator-opts 
-ftree-dse 
-ftree-forwprop 
-ftree-fre 
-ftree-phiprop 
-ftree-sink 
-ftree-slsr 
-ftree-sra 
-ftree-pta 
-ftree-ter 
-funit-at-a-time

 -O2

该优化选项会牺牲部分编译速度,除了执行-O1所执行的所有优化之外,还会采用几乎所有的目标配置支持的优化算法,用以提高目标代码的运行速度。

-fthread-jumps 
-falign-functions  -falign-jumps 
-falign-loops  -falign-labels 
-fcaller-saves 
-fcrossjumping 
-fcse-follow-jumps  -fcse-skip-blocks 
-fdelete-null-pointer-checks 
-fdevirtualize -fdevirtualize-speculatively 
-fexpensive-optimizations 
-fgcse  -fgcse-lm  
-fhoist-adjacent-loads 
-finline-small-functions 
-findirect-inlining 
-fipa-cp 
-fipa-cp-alignment 
-fipa-bit-cp 
-fipa-sra 
-fipa-icf 
-fisolate-erroneous-paths-dereference 
-flra-remat 
-foptimize-sibling-calls 
-foptimize-strlen 
-fpartial-inlining 
-fpeephole2 
-freorder-blocks-algorithm=stc 
-freorder-blocks-and-partition -freorder-functions 
-frerun-cse-after-loop  
-fsched-interblock  -fsched-spec 
-fschedule-insns  -fschedule-insns2 
-fstrict-aliasing -fstrict-overflow 
-ftree-builtin-call-dce 
-ftree-switch-conversion -ftree-tail-merge 
-fcode-hoisting 
-ftree-pre 
-ftree-vrp 
-fipa-ra

-O3

该选项除了执行-O2所有的优化选项之外,一般都是采取很多向量化算法,提高代码的并行执行程度,利用现代CPU中的流水线,Cache等。

-finline-functions      // 采用一些启发式算法对函数进行内联
-funswitch-loops        // 执行循环unswitch变换
-fpredictive-commoning  // 
-fgcse-after-reload     //执行全局的共同子表达式消除
-ftree-loop-vectorize   // 
-ftree-loop-distribute-patterns
-fsplit-paths 
-ftree-slp-vectorize
-fvect-cost-model
-ftree-partial-pre
-fpeel-loops 
-fipa-cp-clone options

这个选项会提高执行代码的大小,当然会降低目标代码的执行时间。

-Os

这个优化标识和-O3有异曲同工之妙,当然两者的目标不一样,-O3的目标是宁愿增加目标代码的大小,也要拼命的提高运行速度,但是这个选项是在-O2的基础之上,尽量的降低目标代码的大小,这对于存储容量很小的设备来说非常重要。

为了降低目标代码大小,会禁用下列优化选项,一般就是压缩内存中的对齐空白(alignment padding)

-falign-functions  
-falign-jumps  
-falign-loops 
-falign-labels
-freorder-blocks  
-freorder-blocks-algorithm=stc 
-freorder-blocks-and-partition  
-fprefetch-loop-arrays

-Ofast:

该选项将不会严格遵循语言标准,除了启用所有的-O3优化选项之外,也会针对某些语言启用部分优化。如:-ffast-math ,对于Fortran语言,还会启用下列选项:

-fno-protect-parens 
-fstack-arrays

-Og:

该标识会精心挑选部分与-g选项不冲突的优化选项,当然就能提供合理的优化水平,同时产生较好的可调试信息和对语言标准的遵循程度。

其他编译参数

-std=c++14启用c++14标准当然对应的GLIBCXX_USE_CXX11_ABI=0也应该被设置

-shared  建立共享library

-rdynamic 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用 dlopen 或者gdb backtrace来实现向后跟踪)

-undefined dynamic_lookup  令所有未定义的符号标记在运行时查找

-fvisibility=hidden 决定共享库中的符号是否可见,如果为hidden有以下优势

  • 提升动态共享对象(DSO(Dynamic Shared Object))的加载效率
  • 使得优化器生成更好的代码**(具体原理待研究)**
  • 减少DSO的大小
  • 避免符号冲突

更新默认gcc
update-alternatives --install /usr/bin/gcc  gcc /usr/bin/gcc-7

update-alternatives --list gcc

性能测试

如图进行的累加测试

程序

-Ofast

C++ Pybind(将循环放入c++中) Pybind(循环放在python中) Python

100w iter 累加

编译命令

c++ -shared -rdynamic -fPIC -undefined  -fvisibility=hidden -std=c++14  $(python3 -m pybind11 --includes)  ex.cpp -o ex$(python3-config --extension-suffix)

0.00227s

0.00234s

↓3.08% (相比纯c++速度略下降)

0.89s

相比循环在C++中速度大幅下降

调用空函数也非常耗时,比纯python慢10倍

0.5149s

0.115s

0.000895s

↑ 60.57% (打开-O优化的提升)

0.000939s

↑ 59.87% (打开-O优化的提升)

0.2044

↑ 77.03%(打开-O优化的提升)

\

1、开启-Ofast对循环优化较大,通过删除循环内没有改变值的变量赋值操作, 减少循环内执行指令的数量。并将中间变量直接分配在寄存器中

2、Pybind11中调用c++函数需要将所有参数都进行类型转换,使用循环频繁调用 c++接口会导致速度下降

3、当循环放在Python代码中没有被优化到,导致速度有所下降

4、Python循环会非常慢主要是python返回的都是可迭代对象内部的__iter__,并且都需记录要状态更新

5、Python中算数是没有类型的,所有在执行__add__操作时候,都必须检测操作数类型(具体可以参考该拿什么拯救你,Slow Python

猜你喜欢

转载自blog.csdn.net/weixin_43953700/article/details/124595296