是否可以在 GCC/Clang 上强制进行尾调用优化?
我正在尝试尽可能地用 C 语言编写函数风格的程序。 我知道像 GCC/Clang 这样的优秀编译器会默默地进行尾调用优化,但这并不能保证。是否有任何选项可以强制编译器进行尾调用优化? (当然,当仅在其自身末尾调用时)
I'm trying to write a program in functional style with C as much as possible.
I know fine compilers like GCC/Clang do tail call optimization silently, but it's not guaranteed. Is there any option to force tail call optimization on the compilers? (Of course when only it's called at the end of itself)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
Clang 13“musttail”属性强制尾部递归函数中的尾部调用优化,即使优化已禁用。
https://clang.llvm.org/docs/AttributeReference.html#musttail
用法:
Clang 13 "musttail" attribute to force tail call optimization in tail recursive functions even if optimizations are disabled.
https://clang.llvm.org/docs/AttributeReference.html#musttail
usage:
Clang 根本没有做任何优化。有一个 LLVM pass
tailcallelim
可以做你想做的事(但不能保证)。您可以使用opt
单独运行它。Clang is not doing any optimisations at all. There is an LLVM pass
tailcallelim
which may do what you want (but it is not guaranteed). You can run it separately withopt
.我不认为它真的强制它,但您可以在使用
gcc
时使用-foptimize-sibling-calls
。如果您使用-O2
、-O3
或-Os
,它会自动启用。一般来说,我建议不要在 C 中过度使用递归。如果你真的想做 fp,请选择函数式语言。在某些情况下它是合适的,例如快速排序,buf,如果你养成使用递归而不是循环的习惯,那么你很有可能会破坏堆栈。
I don't think it really enforces it, but you can use
-foptimize-sibling-calls
when usinggcc
. It's automatically enabled if you use-O2
,-O3
or-Os
.In general, I'd advice against overusing recursion in C. If you really want to do fp, pick a functional language. There are cases where it is suitable, like quicksort, buf if you make a habit out of using recursion instead of loops, chances are pretty high that you will blow the stack.
元答案:
从函数式语言中继承 C 语言有一些有用的教训:使用小函数,使用不会改变全局变量或输入参数的函数,不要害怕函数指针。但是,您在这里可以合理执行的操作是有限的,并且依赖尾部调用消除(“尾部调用优化”并不是真正正确的术语)可能超出了有用的范围。你不能强迫编译器使用这种策略,即使你可以,生成的 C 语言也将非常不惯用,并且很难让其他人(包括未来的你)阅读。
发挥语言的优势。 C对于某些事情很有用,所以以良好的 C 风格来使用它。如果您想要不同的优势,或者如果您想使用函数式风格(非常棒的决定!),请使用函数式语言。
A meta answer:
There are some lessons it's useful to take over into C from functional languages: use small functions, use functions which don't mutate either globals or input arguments, don't be frightened of function pointers. But there's a limit to what you can reasonably do here, and relying on tail-call elimination ('tail-call optimization' isn't really the right term) is probably beyond what's useful. You can't force the compiler to use this strategy, and even if you could, the resulting C would be extremely unidiomatic, and hard to read for others, including your future self.
Use languages to their strengths. C is good for some things, so use it for those, in good C style. If you want different strengths, or if you want to use a functional style (excellent decision!), use a functional language.
Alpha 和 i386 的 GCC 扩展描述于:
C 中的正确尾递归(文凭)论文),作者:Mark Probst,2001 年。
A GCC extension for Alpha and i386 is described in:
Proper Tail Recursion in C (Diploma Thesis) by Mark Probst, 2001.
实际上,许多 C 编译器已经为您处理了这个问题。正如 eq 提到的,您不妨让编译器处理大部分这些事情,而不是尝试创建在其他地方不起作用的优化。很多时候,您会发现即使设置了优化标志,实际上也没有性能差异。
In reality a lot of compilers for C already handle this for you. As eq mentioned you might as well let the compiler handle most of these things rather than trying to create optimizations that won't work elsewhere. Often times you'll find even if you set optimization flags that there is really no performance difference.
如果它确实是尾部调用,那么 while 循环或 goto 看起来与递归调用没有太大不同。只需更新所有变量,而不是将它们作为参数传递。据我所知,这是 C 语言中控制所有优化级别堆栈使用的唯一跨平台方法。它实际上也更具可读性,因为你有一个初始化后跟循环的函数,这是非常惯用的。尾递归版本需要两个函数,一个用于初始化,一个用于递归部分。
If it really is a tail call then a while loop or a goto wont look that much different from a recursive call. Just update all variables instead of passing them as parameters. AFAIK this is the only cross-platform way in C to control stack usage at all optimization levels. It can actually be more readable too since you have one function with initialization followed by the loop, which is pretty idiomatic. The tail recursive version requires two functions, one for initialization and one for the recursive part.