【问题标题】:why optimization does not happen?为什么优化没有发生?
【发布时间】:2010-04-23 18:48:32
【问题描述】:

我有 C/C++ 代码,看起来像这样:

static int function(double *I) {
    int n = 0;
    // more instructions, loops,
    for (int i; ...; ++i)
        n += fabs(I[i] > tolerance);
    return n;
}

function(I); // return value is not used.

编译器内联函数,但它不会优化n 操作。 我希望编译器能够识别出该值永远不会仅用作 rhs。 是否有一些副作用阻止优化?

编译器好像没关系,我试过intel和gcc。积极优化,-O3

谢谢

更完整的代码(完整代码是此类块的重复):

  280         // function registers
  281         double q0 = 0.0;
  282         double q1 = 0.0;
  283         double q2 = 0.0;
  284
  285 #if defined (__INTEL_COMPILER)
  286 #pragma vector aligned
  287 #endif // alignment attribute
  288         for (int a = 0; a < int(N); ++a) {
  289             q0 += Ix(a,1,0)*Iy(a,0,0)*Iz(a,0,0);
  290             q1 += Ix(a,0,0)*Iy(a,1,0)*Iz(a,0,0);
  291             q2 += Ix(a,0,0)*Iy(a,0,0)*Iz(a,1,0);
  292         }
  293 #endif // not SSE
  294
  295         //contraction coefficients
  296         qK0 += q0*C[k+0];
  297         qK1 += q1*C[k+0];
  298         qK2 += q2*C[k+0];
  299
  300         Ix += 3*dim2d;
  301         Iy += 3*dim2d;
  302         Iz += 3*dim2d;
  303
  304     }
  305     Ix = Ix - 3*dim2d*K;
  306     Iy = Iy - 3*dim2d*K;
  307     Iz = Iz - 3*dim2d*K;
  308
  309     // normalization, scaling, and storage
  310     if(normalize) {
  311         I[0] = scale*NORMALIZE[1]*NORMALIZE[0]*(qK0 + I[0]);
  312         num += (fabs(I[0]) >= tol);
  313         I[1] = scale*NORMALIZE[2]*NORMALIZE[0]*(qK1 + I[1]);
  314         num += (fabs(I[1]) >= tol);
  315         I[2] = scale*NORMALIZE[3]*NORMALIZE[0]*(qK2 + I[2]);
  316         num += (fabs(I[2]) >= tol);
  317     }
  318     else {
  319         I[0] = scale*(qK0 + I[0]);
  320         num += (fabs(I[0]) >= tol);
  321         I[1] = scale*(qK1 + I[1]);
  322         num += (fabs(I[1]) >= tol);
  323         I[2] = scale*(qK2 + I[2]);
  324         num += (fabs(I[2]) >= tol);
  325     }
  326
  327
  328     return num;

我唯一的猜测是潜在的浮点异常,它引入了副作用

【问题讨论】:

  • 哪个编译器,什么优化标志?
  • 请将... 部分放入您的代码中。这可能很重要。
  • 你能发布一个完整的版本来显示问题吗?如果我填写循环以从i=0 运行到i=10000 并从否则为空的main 调用它,gcc 似乎优化了所有内容(除了来回调整堆栈指针 - 不确定为什么它会留下这个:))
  • 另一方面,您可能想要fabs(I[i]) &gt; tolerance,而不是fabs(I[i] &gt; tolerance)。前者检查 I[i] 的绝对值是否大于容差,后者测试有符号值并在 0 或 1 上执行不必要的fabs()
  • 我可能独自一人,但这是我没有用的优化,我希望人们不会被引导去期待。人们是否编写复杂的代码只是为了看看编译器是否足够聪明编译它?如果我写这样的东西,我宁愿如果编译器做了什么,它应该给我一个严厉的警告。

标签: c++ c optimization compiler-construction


【解决方案1】:

代码确实使用了n,首先将其初始化为0,然后在可能产生副作用的函数左侧的循环内(fabs)。

你是否实际使用函数的return无关紧要,n本身使用了。

更新:我在 MSVC10 中尝试了这段代码,它优化了整个功能。给我一个完整的例子,我可以试试。

#include <iostream>
#include <math.h>

const int tolerance=10;

static int function(double *I) {
    int n = 0;
    // more instructions, loops,
    for (int i=0; i<5; ++i)
        n += fabs((double)(I[i] > tolerance));
    return n;
}


int main()
{
    double I[]={1,2,3,4,5};

    function(I); // return value is not use
}

【讨论】:

  • 是的,但 n 仅用于自身,它不用作任何表达式中的右手值。我希望编译器能够认识到这一点
  • 内联后的代码看起来像int n = 0; for(...) n+=...;n 在该循环之后不再使用。如果我没有遗漏什么,它可能会产生fabs 的可能副作用,而无需为n 使用存储空间。它甚至可以知道fabs(参见-fno-builtin),并优化整个循环。但显然它并没有这样做。
  • 详细说明:即使它是内联的,编译器也只需要生成一个版本的function,如果它生成的版本没有返回任何内容,你将永远无法真正调用当您确实需要返回值时使用它。
  • @Mark B:OP 似乎正在检查内联代码,而不是使用正文生成的版本。为什么n 没有从内联代码中删除确实不清楚。显然,编译器不够聪明。此外,如果函数没有显式要求主体(例如通过指针调用),编译器通常不会为内联函数生成独立主体
  • @Blindy:编译器确实知道当函数调用内联时不使用返回值,正如OP明确声明的那样。跨度>
【解决方案2】:

我认为这个问题的简短答案是,仅仅因为编译器可以在理论上进行一些优化并不意味着它。没有什么是免费的。如果编译器要优化掉 n,那么就必须有人编写代码来完成它。

对于一个奇怪的角落案例和微不足道的空间节省来说,这听起来需要做很多工作。我的意思是,人们多久编写一次执行复杂计算的函数只是为了丢弃结果?在这种情况下是否值得编写复杂的优化来恢复 8 字节的堆栈空间?

【讨论】:

  • 鉴于最近发布的代码,这可能是正确的答案。编译器不是魔法机器。
  • 在一般情况下,这并不少见。一个函数可能会生成两个返回值,您实际上只关心其中一个。或者函数的实际工作可能与返回值完全正交;例如,构建一个数组并返回数组中值的数量的函数。如果您不关心数组中值的数量,只要构建了数组,那么您可能会完全忽略返回值。
  • 我同意忽略返回值是很正常的。但在这种情况下,返回值通常是微不足道的,就像在您的数组示例中一样。这意味着您可以通过优化它来节省一小笔钱。然而,进行这种优化的代码会很复杂。
  • @peter 我认为不必要的代码路径将显示为未连接到任何东西的解析树,编译器会丢弃。这似乎是一个很常见的场景
  • 我不这么认为,但由于不了解您使用的编译器的内部工作原理,我无法进一步评论。
【解决方案3】:

我不能肯定它会产生影响,但您可能需要查看 GCC 的 pureconst 属性 (http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html)。它基本上告诉编译器该函数只对其输入进行操作并且没有副作用。

鉴于这些额外信息,它可能能够确定调用是不必要的。

【讨论】:

  • 他们似乎添加了更多可能对我有用的优化选项。谢谢
  • 我假设你想在输入函数(const double *I) 上加上一个 const 以保证正确性,也许它有助于编译器优化,直到你尝试才知道。
  • @dweltch:虽然您给出的建议是正确的。我实际上指的是一个名为 const 的 GCC 属性。有关详细信息,请参阅我发布的链接。
【解决方案4】:

尽管我在其他线程中遇到过争论,所有编译器都是完美的,并且不会错过任何优化。编译器并不完美,通常不会捕捉到优化。

像这样有趣的:

诠释乐趣(诠释一个) { 开关(a&3) { 案例 0:返回(a+4); 案例1:返回(a+2); 案例2:return(a); 案例3:返回(0); } 返回(1); }

如果你在最后离开那个返回值,那么很长一段时间你会得到一个错误,即函数定义了一个返回类型但没有返回一个值。一些编译器会在函数末尾抱怨 return() 并抱怨没有它。

根据我从 gcc 和 llvm 中可以看出,gcc 在文件中的一个函数内进行优化,而 llvm 对它所提供的所有内容进行优化。您可以将整个项目的字节码合并到一个文件中,并一次性优化整个项目。目前 gcc 的输出比 llvm 高出十几个百分点,这很有趣。给它时间。

也许在您的情况下,您正在使用两个未声明为静态(const)的输入进行计算,因此结果 n 可能会发生变化。如果在每个功能的基础上进行优化,它就不能进一步减少它。所以我的猜测是它正在优化每个函数并且调用函数不知道什么影响我在系统上的动态输入,即使没有使用返回值它仍然需要计算函数(I)来解决任何依赖I. 我假设这不是一个无限循环,... 意味着施加了一些限制?如果这里不再是动态的而不是静态的,function(I) 可能是一个终止的无限循环函数,或者它可能在那里等待中断服务例程修改 I 并将其踢出无限循环。

【讨论】:

    猜你喜欢
    • 2017-05-15
    • 2017-07-18
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多