C / C ++编译器是否会对可交换运算符(例如:+,*)进行重新排序以优化常量

时间:2015-10-02 03:59:00

标签: c++ optimization

以下代码的第二行

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);

保存括号。 (并且减轻了手动操作那些常量排序的需要=>并且在许多情况下,使用相关变量进行分组常量更有意义,而不是将常量分组以进行优化)

3 个答案:

答案 0 :(得分:7)

几乎所有编译器都会为整数执行此操作,因为即使常量崩溃可能以不同的方式溢出,标准也可能会忽略溢出,因此他们可以按照自己喜欢的方式执行。

如果它符合严格的浮点数学,它通常不适用于浮点值;浮点数学的评估顺序会影响结果,因此严格的合规性不能对浮点数学进行重新排序。

  

5.1.2.3程序执行

     

[#1]本国际标准中的语义描述描述了抽象机器的行为,其中优化问题无关紧要。

     

[#3]在抽象机器中,所有表达式都按语义指定进行评估。

     

[#13]示例5由于精度和范围的限制,浮点表达式的重排通常受到限制。即使在没有溢出和下溢的情况下,由于舍入误差,实现通常也不能应用加法或乘法的数学关联规则,也不能应用分布规则。 (Source

它没有准确地描述常量的用法,但它清楚地指出,看似等效的操作在浮点运算的奇异世界中实际上并不等效(例如x / 5.0不能已完全等效翻译为x * 0.2x + 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;,以及进一步的例子贯穿整个文件。