【问题标题】:gcc transformation pass for ackermann阿克曼的 gcc 转换通行证
【发布时间】: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


【解决方案1】:

第一遍是尾调用消除。 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 级别将其内联了几次迭代。导致你看到的巨大怪物。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-28
    • 1970-01-01
    • 2012-01-31
    • 2015-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多