C++编译优化那些事儿
关于C++编译优化的一些探究
问题
今天在群里看到这么个问题,就是下面这个代码在执行时每次判断都要计算一次sqrt函数吗?
我猜想群里一直探讨的是某种语言的原生编译器情况下(不带任何编译优化选项的情况下)编译完成后执行程序,这个sqrt()是否会只计算一次。
对于别的语言我不熟,但是我知道C++的开了-Ox优化一定是可以优化的,其实这是个老生常谈的问题,那就是编译器优化。
验证
启动Compiler Explorer (godbolt.org),看下汇编代码就行,输入以下代码:
1 |
|
看一下x86-64 gcc 6.4的汇编代码,可以发现每次都调用了sqrt函数:
-O2的威力
如果我们加入了-O2
选项,则整个代码会被直接优化为空:
1 |
|
经过验证,-O2
太过强悍,基本可以优化所有的架构+编译器下的类似代码
constexpr的威力
我们发现C++代码中局部变量n的值是一个普通的整型,我们考虑对其使用constexpr
(或者const)修饰,看看是否有改变,输入以下代码:
1 |
|
看一下x86-64 gcc 6.4的汇编代码,可以发现for的小于判断处已经被.data段常量替代了,每次仅需从代码段读取该数据到寄存器中而不需要耗费调用函数的开销:
当升级到x86-64 gcc 8.1以后,这个值在程序编译完成后的汇编代码中是直接被常量值替换的(sqrt(10)直接替换为3):
当然,这个优化并不是所有编译器都有(但是-O2是都有的),例如,如果采用x86 msvc v19.36或者更高版本的msvc,还是会每次判断都调用一次sqrt:
同时,x86-64 clang 15.0.0也是一样:(虽然都会把值放到常量区)
结论
经验证:
-O2
基本可以优化所有的类似行为,并且优化力度极大,对于所有架构和所有类型的编译器均有效(目前没有发现例外,x86、arm、risc-v下的llvm、clang、gcc、msvc均受益)- x86-64下的GCC 在constexpr或者const的加持下可以实现只计算一次的优化
- x86-64下的GCC 7.5 -> 8.1的升级,将 结果常量存储在代码段 升级为 汇编代码中结果常量值直接替换,达到进一步优化(不需要从代码段加载)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!