G++ 编译器对非交换操作的优化程度

Extent of G++ compiler optimization on non-commutative operations

我担心 G++ 优化器对算术运算的影响,特别是不一定可交换的整数运算,例如 * 和 /。当我在 gdb 中查看一个使用 -O3 标志集编译的简单函数时,出现了这个问题;总而言之,它是一个更好的功能,但它的形式与没有优化的形式完全不同,操作已被删除,并且一些已被重新定位。这是一个简单的函数,我将用它来证明我关注的症结所在;

int ClipLower(int num, int dig){
  int Mult10 = 1;
  while (dig != 0){
    Mult10 *= 10, dig--;
  }
  return ((num / Mult10) * Mult10);
}

这个函数简单地剪掉数字 'dig' 以下的 base10 数字。我担心的是,编译器是否考虑了整数数学是不可交换的事实?那么,编译器是否会尝试将 (num / mult10) * mult10 减少为 num * 1,当然会丢弃一个?

我知道 volatile 会避免这种情况,但我仍然希望尽可能优化我的代码。所以从本质上讲,我是在问 gnu 优化器是否会理解整数数学是不可交流的,还有更多的问题是优化出错了。

还有

这里是-O4处函数的反汇编,可以看到,操作顺序没问题

  13        return ((num / Mult10) * Mult10);
       cltd
       idiv   %ecx
       imul   %ecx,%eax
       ret

有趣的是,编译器在函数之后生成了一大堆空操作,可能是作为填充,因为它最终变得如此之小。

试试看组装

优化应该不会影响输出,只会影响速度。应保留四舍五入。但是可能会出现错误,尽管现在很少见。

一般来说,浮点数更有可能出现问题。 floats 的 2/7 可能略有不同。

对于ints它应该总是0,无论什么优化,即使它乘以7。

这是 g++ 中 -O3 等同于的标志列表:https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

现在如果你仔细看,还有 -Ofast 定义为 -O3 + 一些其他的,尤其是 -ffast-math。在 -ffast-math 的描述中,您可以阅读:

This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.

这样做是为了确保默认编译器标志不违反舍入误差和其他浮点标准规范。

SO上也有一个相关的问题,why don't compilers optimize a*a*a*a*a*a to (a*a*a)^2,答案是一样的。 (我找不到 link atm =/)

顺便说一句,Mult10 *= 10, dig--; 你是想失去遵循你的代码的人吗? =D

编辑: 另一个顺便说一句,超过 -O3 没有效果。除了有人说你可能会溢出一些内部变量。我没有测试溢出,但我确定 -O4-O100 在撰写本文时等同于 -O3