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

C++ 将C/C++;编译器对交换运算符(例如:+;,*)进行重新排序以优化常量,c++,optimization,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); 保存括号。(这样就不需要手动操作这些常量排序=>并且在许多情况下,将常量与相关变量分组比将常量分组以进行优化更有意义)给定的实现可能会优化这些

下面代码的第二行

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.4
,它将行为定义为“外部的外观或动作”,进一步的例子贯穿了整个文档。

几乎所有的编译器都会对整数这样做,因为即使常数崩溃可能以不同的方式溢出,溢出也可能被标准忽略,所以他们可以随心所欲


如果坚持严格的浮点数学,它通常不适用于浮点值;浮点数学的求值顺序可能会影响结果,因此严格遵从性不能重新排序浮点数学

5.1.2.3程序执行

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

[#3]在抽象机器中,所有表达式都按照语义指定的方式进行计算

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


它并没有精确地描述常数的用法,但它清楚地指出,在bizarro的浮点运算世界中,看似等价的运算实际上并不等价(例如,
x/5.0
不能完全等价地转换为
x*0.2
x+x*y
不能等价地表示为
x*(1.0+y)
)。

下面是一个优化器的示例。使用-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;
}
int计算(int条)
{

返回(bar@user657267,如果
gcc
int foo=3*bar*5;
优化为
bar*45
,则它将严重不足:-)当然,现在你已经编辑了评论,这是毫无意义的。@pedants很抱歉输入错误。@M.M:可能不是很明确,但是如果你用
\define
s来命名你的常数,而不是到处都是神奇的数字,你可能会看到
int foo=bar*frobnate\u COUNT*WIDGET\u headloom;
(我的名字很有趣)其中,
#define
会导致预处理器分别生成
3
5
。如果混合中有未初始化的变量,则优化并不重要。但您可能只是想指示变量的类型。@ShadowRanger:“但是,如果您使用
#define
s来命名常量,而不是在任何地方都使用幻数”,尽管最佳做法是避免不必要地使用预处理器define,但
const
变量也可以(我知道这并不普遍,但不加区别地鼓励宏是不好的)…”严格遵从性无法重新排序浮点数学",请您添加标准中的支持性引用。这对我来说是新闻。@Alf:Done。我在标准中找不到常数的具体引用,但gcc的一位开发人员介绍了2004年各种语言的状态。浮点数的一个例子是:
-O0
输出。没有自尊心的优化ing编译器会像那样将edi溢出到堆栈中。而且,
-fomit帧指针
已经是默认值一段时间了。使用
echo'intf(intn){return n*3*5;}“| gcc-xc--O3-S-o-
,或者更好。不过,函数的核心将编译为与清单中相同的3mov/shl/sub指令。@PeterCordes:谢谢,我的构建中有一个错误。现在已经修复了。