c++编译器对非交换运算的优化程度

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

本文关键字:优化 程度 运算 交换 编译器 c++      更新时间:2023-10-16

我很关心g++优化器对算术运算的影响,特别是不一定可以交换的整数运算,例如eg *和/。当我在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
有趣的是,编译器在函数之后生成了一个无操作的负载,可能是作为填充,因为它最终非常小。

下面是g++中-O3对应的标志列表:https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

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

此选项不能被除-Ofast之外的任何-O选项打开,因为对于依赖于IEEE或ISO规则/数学函数规范的精确实现的程序,它可能导致错误的输出。然而,对于不需要这些规范保证的程序,它可能产生更快的代码。

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

在SO上也有一个相关的问题,为什么编译器不把a*a*a*a*a*a优化到(a*a*a)^2,答案是一样的。(I cannot find the link atm =/)

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

EDIT:另一个顺便说一下,通过-O3没有效果。除了有人说你可能会溢出一些内部变量。我没有测试溢出,但我确信-O4-O100在写这篇文章的时候相当于-O3

试一下,看看程序集

优化不应该影响输出,只影响速度。应保持舍入。但是bug还是会发生的,尽管现在已经很少了。

通常问题更可能出现在浮点数上。2/7与float s可能略有不同。

对于int s,无论进行什么优化,它都应该始终为0,即使它乘以7。