【发布时间】:2013-04-24 14:20:24
【问题描述】:
gcc(我在 Mac 和 Linux 上使用 -O3 标志尝试了 4.7.2)将 ackermann 函数优化为具有大本地堆栈的单个调用。下面是 Ackermann 代码示例:
int ack(int m,int n){
if(m == 0) return n+1;
if(n == 0) return ack(m-1,1);
return ack(m-1,ack(m,n-1));
}
当反汇编时,只有一次递归调用 ack 函数,而不是两次调用(我无法解析发生的事情 -ack 现在被 gcc 转换为具有 8 个参数的函数,并且本地49 个 int 和 9 个 char 的堆栈)。我试图查找 gcc 为将 Ackermann 函数优化为单个调用所做的转换传递,但没有找到任何感兴趣的东西。我将欣赏有关 gcc 执行哪些主要转换以将深度递归 Ackermann 转换为单个递归调用的指针。 LLVM gcc(我在 mac 上尝试过 v4.2)还没有将其减少为单个递归调用,并且使用-O3 标志慢了 4 倍。这个优化看起来很有趣。
【问题讨论】:
-
您还应该看到速度在该系统和 gcc 版本(带有 -O1 和 -O2)上的位置。手册页列出了很多(如果不是全部)在每个步骤中完成的优化,所以如果它是其中一个特定的东西,你也许可以缩小范围。
-
在终端递归的情况下,通过转到函数开头(没有检查是否发生这种情况)替换对函数本身的调用并不难。
-
@MarcGlisse,Ackermann 是深度递归函数的一个特例。我知道其他递归函数(如斐波那契)的尾递归技术。将斐波那契转换为尾递归函数非常简单,但据我所知不是阿克曼。
-
这就是为什么还剩下一个调用而不是0...在最后一次调用ack之后,您不需要回到调用函数,因此您可以将调用替换为跳跃。
-
好像是兄弟调用优化。在 say -O1 中仅打开 foptimize-sibling-calls 标志,重现此优化,然后将其关闭,将其关闭。如果有人对如何在 Ackermann 函数的上下文中完成这种转换有更多的指示,请随时回答。
标签: optimization gcc