【问题标题】:Is it possible to force tail call optimization on GCC/Clang?是否可以在 GCC/Clang 上强制进行尾调用优化?
【发布时间】:2011-01-24 17:32:27
【问题描述】:

我正在尝试尽可能多地使用 C 编写函数式程序。 我知道像 GCC/Clang 这样的编译器会默默地进行尾调用优化,但不能保证。是否有任何选项可以强制对编译器进行尾调用优化? (当然只有在它自己的末尾才被调用)

【问题讨论】:

  • 编译器在这方面可能相当聪明,相信它。无需不可移植 hacks。
  • 在您认为应该进行尾部优化但编译器无法执行此操作(无论出于何种原因)的情况下,您希望发生什么?
  • @Michael 如果无法完成强制尾调用优化,我预计会出现编译时错误。
  • 您可以强制 GCC 为 TCO 提供编译器标志:-foptimize-sibling-calls(参见draketo.de/light/english/free-software/tco-debug

标签: c clang tail-call-optimization


【解决方案1】:

Clang 13 “musttail”属性强制在尾递归函数中进行尾调用优化,即使优化被禁用。

https://clang.llvm.org/docs/AttributeReference.html#musttail

用法:

int f(int x) {
  ...
  __attribute__((musttail)) return f(x-1);
}

【讨论】:

    【解决方案2】:

    Clang 根本没有做任何优化。有一个 LLVM 通行证 tailcallelim 可以做你想做的事(但不能保证)。您可以使用opt 单独运行它。

    【讨论】:

    • 有什么选择?能给个链接吗?
    • 另外,您可以调整 clang 驱动程序以确保它明确地运行此通道。
    【解决方案3】:

    我不认为它真的强制它,但你可以在使用gcc 时使用-foptimize-sibling-calls。如果您使用-O2-O3-Os,它会自动启用。

    一般来说,我建议不要在 C 中过度使用递归。如果您真的想做 fp,请选择一种函数式语言。在某些情况下它是合适的,比如快速排序、buf,如果你习惯于使用递归而不是循环,那么你很有可能会破坏堆栈。

    【讨论】:

      【解决方案4】:

      元答案:

      从函数式语言过渡到 C 语言有一些有用的经验:使用小函数,使用不会改变全局变量或输入参数的函数,不要害怕函数指针。但是你在这里可以合理地做的事情是有限制的,依赖尾调用消除('tail-call 优化'并不是真正正确的术语)可能超出了有用的范围。你不能强迫编译器使用这种策略,即使你可以,生成的 C 语言也会非常不习惯,并且很难被其他人阅读,包括你未来的自己。

      充分利用语言的优势。 C 对某些事情有好处,所以以良好的 C 风格将它用于那些事情。如果您想要不同的优势,或者如果您想使用函数式风格(绝妙的决定!),请使用函数式语言。

      【讨论】:

        【解决方案5】:

        Alpha 和 i386 的 GCC 扩展如下所述:

        Proper Tail Recursion in C (Diploma Thesis)Mark Probst,2001 年。

        【讨论】:

          【解决方案6】:

          实际上,很多 C 编译器已经为您处理了这个问题。正如 eq 所提到的,您不妨让编译器处理大部分这些事情,而不是尝试创建在其他地方不起作用的优化。很多时候,即使设置了优化标志,您也会发现实际上并没有性能差异。

          【讨论】:

          • 要强制执行尾调用优化的常见情况是线程代码。在这种情况下,函数有时会返回,但通常会调用具有相同签名(相同参数、相同返回值)的其他函数。一个标准的函数调用序列将运行程序超出堆栈空间并几乎立即崩溃,所以这不是我们可以留给编译器的事情。
          【解决方案7】:

          如果它真的是尾调用,那么 while 循环或 goto 看起来与递归调用没有太大区别。只需更新所有变量,而不是将它们作为参数传递。 AFAIK 这是 C 语言中在所有优化级别控制堆栈使用的唯一跨平台方式。它实际上也更具可读性,因为您有一个初始化函数,然后是循环,这是非常惯用的。尾递归版本需要两个函数,一个用于初始化,一个用于递归部分。

          【讨论】:

          • 这仅适用于 TCO 遵循单一路径到达终点的琐碎情况。当用递归表示时,分支出许多不同递归路径的递归函数会更清晰。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-06-25
          • 1970-01-01
          • 2010-10-23
          • 2012-04-18
          • 2011-07-16
          • 2018-11-29
          • 1970-01-01
          相关资源
          最近更新 更多