https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.htmlhttps://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
一般来说,如果不指定优化标识的话,gcc就会产生可调试代码,每条指令之间将是独立的:可以在指令之间设置断点,使用gdb中的 p命令查看变量的值,改变变量的值等。并且优先采用最快的编译速度
当优化标识被启用之后,gcc编译器将会改变程序的结构(当然会在保证变换之后的程序与源程序语义等价的前提之下),以满足代码大小最小或运行速度更快,当然二者是不可兼得的。在不同的gcc配置和目标平台下,同一个标识所采用的优化种类也是不一样的。-O会在大大优化循环中的运行速度。
这两个命令的效果是一样的,目的都是在不影响编译速度的前提下,尽量采用一些优化算法降低代码大小和可执行代码的运行速度。并开启如下的优化选项:
该优化选项会牺牲部分编译速度,除了执行-O1所执行的所有优化之外,还会采用几乎所有的目标配置支持的优化算法,用以提高目标代码的运行速度。
该选项除了执行-O2所有的优化选项之外,一般都是采取很多向量化算法,提高代码的并行执行程度,利用现代CPU中的流水线,Cache等。
这个选项会提高执行代码的大小,当然会降低目标代码的执行时间。
这个优化标识和-O3有异曲同工之妙,当然两者的目标不一样,-O3的目标是宁愿增加目标代码的大小,也要拼命的提高运行速度,但是这个选项是在-O2的基础之上,尽量的降低目标代码的大小,这对于存储容量很小的设备来说非常重要。
为了降低目标代码大小,会禁用下列优化选项,一般就是压缩内存中的对齐空白(alignment padding)
该选项将不会严格遵循语言标准,除了启用所有的-O3优化选项之外,也会针对某些语言启用部分优化。如:-ffast-math ,对于Fortran语言,还会启用下列选项:
该标识会精心挑选部分与-g选项不冲突的优化选项,当然就能提供合理的优化水平,同时产生较好的可调试信息和对语言标准的遵循程度。
Cmake中添加方法
-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)