编译器中的优化
编译器如何优化
现代编译器能够对代码做很多修改,以提高性能。了解编译器可以做什么,不可以做什么,对开发者来说是有用的。下边的小节描述了开发者需要了解的一些编译器优化的内容。
函数内联
编译器可以用被调用函数体来替换原来的函数调用。例子:
// Example 8.1a
float square (float a) {
return a * a;
}
float parabola (float x) {
return square(x) + 1.0f;
}
编译器可以用square里的代码替换对square的调用:
// Example 8.1b
float parabola (float x) {
return x * x + 1.0f;
}
函数内联的好处有:
- 消除了调用与返回以及参数传递的开销。
- 代码缓存更好,因为代码变得连续。
- 如果被内联函数仅有一处调用,代码变得更小。
- 函数内联可以为其他优化创造机会,如下面解释那样。
函数内联的坏处是,如果被内联函数有多处调用且该函数大,代码变得更大。如果函数小且仅从一处或少数几个地方调用,编译器很可能内联它。
常量折叠和常量传播
仅包含常量的表达式或子表达式将被计算后的结果替代。
指针消除
如果指向目标已知,指针或引用可以被消除。例子:
。。。
公共子表达式消除
如果同一个子表达式出现多次,编译器可能仅计算一次。例子:
。。。
寄存器变量
最常用的变量保存在寄存器里(参考寄存器存储)。
在32位系统中,整数寄存器变量最大数量是大约6个,在64位系统中是14个。
在32位系统中,浮点寄存器变量最大数量是8个,在64位系统中是16个(如果AVX512指令集可用,则是32个)。在32位系统中,某些编译器创建浮点寄存器变量有困难,除非启用SSE2(或更新的)指令集。