哪一个,如果有的话,C ++编译器做尾recursion优化?

在我看来,在C和C ++中进行尾recursion优化非常合适,但在debugging时,我从来没有看到指示此优化的帧堆栈。 这样很好,因为堆栈告诉我recursion有多深。 但是,优化也会很好。

有没有任何C ++编译器做这个优化? 为什么? 为什么不?

我该如何去告诉编译器这样做?

  • 对于MSVC:/ O2或/ Ox
  • 对于GCC:-O2或-O3

如何检查编译器是否在某种情况下做了这个?

  • 对于MSVC,启用PDB输出以便能够跟踪代码,然后检查代码
  • 对于GCC ..?

我仍然会对如何确定某个函数是否由编译器进行优化提供build议(尽pipe我发现Konrad让我承认它)

总是有可能通过无限recursion检查编译器是否做到这一点,并检查是否导致无限循环或堆栈溢出(我用GCC做了这个,发现-O2足够了),但是我想能够检查某个我知道会终止的函数。 我很想有一个简单的方法来检查这:)


经过一些testing,我发现破坏者破坏了这个优化的可能性。 在返回语句开始之前,有时可能需要更改某些variables和临时对象的范围,以确保它们超出范围。

如果在尾部呼叫之后需要运行任何析构函数,则不能进行尾部呼叫优化。

VC ++和GCC的当前版本都相当好地调用优化,甚至是相互recursion的调用。 我敢打赌英特尔编译器也是。

让编译器进行优化很简单:只需打开优化速度即可。 不过你呢,对吧? 😉

  • 对于MSVC,使用/ O2或/ Ox。
  • 对于GCC,使用-O3

检查编译器是否执行了优化(我知道)的最简单方法是执行调用,否则会导致堆栈溢出 – 或者查看程序集输出。 但是,通常你只能假设编译器做了优化。

/编辑:在Mark Probst的gradle论文中 ,C语言的尾部呼叫优化已被添加到GCC中。 论文描述了一些有趣的警告在实施。 这是值得一读的。

gcc 4.3.2完全将这个函数(蹩脚/简单的atoi()实现)内联到main() 。 优化级别是-O1 。 我注意到,如果我玩弄它(即使将其从static变为extern ,尾recursion也会相当快地消失,所以我不会依赖它来保证程序的正确性。

 #include <stdio.h> static int atoi(const char *str, int n) { if (str == 0 || *str == 0) return n; return atoi(str+1, n*10 + *str-'0'); } int main(int argc, char **argv) { for (int i = 1; i != argc; ++i) printf("%s -> %d\n", argv[i], atoi(argv[i], 0)); return 0; } 

大多数编译器在debugging版本中不做任何forms的优化。

如果使用VC,尝试使用PDB信息打开版本生成 – 这将让你跟踪优化的应用程序,你应该希望看到你想要的。 但是请注意,debugging和跟踪经过优化的版本会在各处跳跃,并且通常不能直接检查variables,因为它们只能以寄存器forms存在或完全被优化。 这是一个“有趣”的经验…

除了显而易见的(编译器不会做这种优化,除非你要求),C ++中的tail-call优化是一个复杂的过程:析构函数。

给定类似于:

  int fn(int j, int i) { if (i <= 0) return j; Funky cls(j,i); return fn(j, i-1); } 

编译器不能(通常)tail-call优化这个,因为它需要在recursion调用返回之后调用cls的析构函数。

有时编译器可以看到析构函数没有外部可见的副作用(所以可以尽早完成),但通常不能。

这是一个特别常见的forms, Funky实际上是一个std::vector或类似的东西。

正如Greg所说,编译器不会在debugging模式下执行。 debugging版本可以比prod版本慢,但它们不应该更频繁地崩溃:如果你依赖于尾部调用优化,他们可以做到这一点。 正因为如此,通常最好将尾部调用重写为正常循环。 🙁