gcc
(我在Mac上尝试了4.7.2
并且使用-O3
标志的Linux)将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倍。这种优化似乎非常有趣。
答案 0 :(得分:7)
第一遍是尾部呼叫消除。 GCC在大多数优化级别上都这样做。基本上,尾部位置的所有函数调用都转换为goto,如下所示:
int ack(int m, int n) {
begin:
if (m == 0) return n + 1;
if (n == 0) { m -= 1; n = 1; goto begin; }
n = ack(m, n-1); m -= 1; goto begin;
}
现在只剩下一个递归调用,GCC只在-O3级别,可以在几次迭代中内联。导致你看到的巨大怪物。