以下代码的第二行
int bar;
int foo = bar * 3 * 5;
优化到
int bar;
int foo = bar * 15;
甚至更多:
int foo = 3 * bar * 5;
可以优化吗?
目的实际上是问我是否可以写
int foo = bar * 3 * 5;
而不是
int foo = bar * (3 * 5);
保存括号。 (并且减轻了手动操作那些常量排序的需要=>并且在许多情况下,使用相关变量进行分组常量更有意义,而不是将常量分组以进行优化)
答案 0 :(得分:7)
几乎所有编译器都会为整数执行此操作,因为即使常量崩溃可能以不同的方式溢出,标准也可能会忽略溢出,因此他们可以按照自己喜欢的方式执行。
如果它符合严格的浮点数学,它通常不适用于浮点值;浮点数学的评估顺序会影响结果,因此严格的合规性不能对浮点数学进行重新排序。
5.1.2.3程序执行
[#1]本国际标准中的语义描述描述了抽象机器的行为,其中优化问题无关紧要。
[#3]在抽象机器中,所有表达式都按语义指定进行评估。
[#13]示例5由于精度和范围的限制,浮点表达式的重排通常受到限制。即使在没有溢出和下溢的情况下,由于舍入误差,实现通常也不能应用加法或乘法的数学关联规则,也不能应用分布规则。 (Source)
它没有准确地描述常量的用法,但它清楚地指出,看似等效的操作在浮点运算的奇异世界中实际上并不等效(例如x / 5.0
不能已完全等效翻译为x * 0.2
,x + x * y
无法等同地表示为x * (1.0 + y)
)。
答案 1 :(得分:3)
以下是优化程序将执行的操作的示例。使用-O2:
使用g ++ 4.9.2编译此代码int calculate(int bar)
{
return bar*3*5;
}
被翻译成这个汇编代码:
movl %edi, %eax # copy argument into eax
sall $4, %eax # shift eax left 4 bits
subl %edi, %eax # subtract original value from eax
ret # return (with eax as result)
它不仅没有进行两次乘法,甚至没有进行两次乘法。它将乘法乘以15转换成与此相当的东西:
int calculate(int bar)
{
return (bar<<4)-bar;
}
答案 2 :(得分:2)
给定的实现可以优化或不优化任何这些表达式。如果您真的想知道它为给定的输入集做了什么,请检查生成的汇编代码。
但是,从现在开始的一周后,我们无法保证您能从其他编译器获得相同的优化,同一编译器使用不同的选项,甚至是完全相同的编译器/选项。
要遵循的一般规则是 &#34;好像&#34; 规则,编译器就好像一样完全符合标准中规定的内容。这并不意味着它必须以任何给定的方式进行。
换句话说,编译器可以自由地做任何想做的事情,只要它具有与标准规定的效果相同的效果。
标准实际上很早就开始关注这方面,在定义部分3.4
中,它将行为定义为&#34; 外部 外观或行动&#34;,以及进一步的例子贯穿整个文件。