当前位置:  开发笔记 > 编程语言 > 正文

哪个(如果有的话)C++编译器会进行尾递归优化?

如何解决《哪个(如果有的话)C++编译器会进行尾递归优化?》经验,为你挑选了5个好方法。

在我看来,在C和C++中进行尾递归优化是完美的,但是在调试时我似乎永远不会看到表示此优化的帧堆栈.这有点好,因为堆栈告诉我递归的深度.但是,优化也会很好.

是否有任何C++编译器进行此优化?为什么?为什么不?

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

对于MSVC:/O2/Ox

对于海湾合作委员会:-O2-O3

如何在某种情况下检查编译器是否已完成此操作?

对于MSVC,启用PDB输出以跟踪代码,然后检查代码

对于GCC ..?

我仍然会建议如何确定编译器是否对某个函数进行了优化(尽管我发现它让人放心,Konrad告诉我假设它)

总是可以通过进行无限递归来检查编译器是否完成此操作,并检查它是否导致无限循环或堆栈溢出(我用GCC做了这个并且发现这-O2已经足够了),但我想成为能够检查我知道的某个功能无论如何都会终止.我很想有一个简单的方法来检查这个:)


经过一些测试,我发现析构函数破坏了进行优化的可能性.有时可能值得更改某些变量和临时值的范围,以确保它们在return语句开始之前超出范围.

如果在尾调用后需要运行任何析构函数,则无法进行尾调用优化.



1> Konrad Rudol..:

所有当前的主流编译器都能很好地执行尾调优化(并且已经完成了十多年),即使对于相互递归的调用,例如:

int bar(int, int);

int foo(int n, int acc) {
    return (n == 0) ? acc : bar(n - 1, acc + 2);
}

int bar(int n, int acc) {
    return (n == 0) ? acc : foo(n - 1, acc + 1);
}

让编译器进行优化非常简单:只需开启优化速度:

对于MSVC,请使用/O2/Ox.

对于GCC,Clang和ICC,请使用 -O3

检查编译器是否进行优化的一种简单方法是执行一个调用,否则会导致堆栈溢出 - 或者查看程序集输出.

作为一个有趣的历史记录,在Mark Probst的毕业论文中,C的尾调优化被添加到GCC .论文描述了实施中的一些有趣的警告.值得一读.


@Paul问题在于ICC代码的速度有多少是由算法优化引起的,例如尾调用优化以及缓存和微指令优化造成了多少,只有英特尔对自己的处理器有深入了解才能做到.
`gcc`有更多的选项`-foptimize-sibling-calls`来"优化兄弟和尾递归调用".此选项(根据`gcc(1)`针对各种平台的版本4.4,4.7和4.8的手册页)在级别`-O2`,`-O3`,`-Os`中启用.

2> Tom Barta..:

gcc 4.3.2完全将这个函数(蹩脚/平凡的atoi()实现)内联到了main().优化级别是-O1.我发现,如果我用它玩(甚至改变它staticextern时,尾递归消失非常快,所以我不会依赖于它的程序的正确性.

#include 
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;
}


奇怪.我刚刚测试了gcc 4.2.3(x86,Slackware 12.1)和gcc 4.6.2(AMD64,Debian wheezy)和**和`-O1`****没有内联**和**没有尾递归优化**.你必须**为那个**使用`-O2`(好吧,在4.2.x中,现在相当古老,它仍然不会被内联).BTW还值得补充的是,gcc可以优化递归,即使它不是严格的尾部(如阶乘w/o累加器).

3> Martin Bonne..:

除了显而易见之外(编译器不会进行这种优化),C++中的尾调用优化存在一个复杂性:析构函数.

给出如下内容:

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

编译器不能(通常)尾调用优化它,因为它需要在递归调用返回cls 调用析构函数.

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

一种特别常见的形式Funky是实际上是一个std::vector或类似的地方.



4> Greg Whitfie..:

大多数编译器在调试版本中不进行任何类型的优化.

如果使用VC,请尝试打开PDB信息的版本构建 - 这将允许您跟踪优化的应用程序,您应该希望看到您想要的.但请注意,调试和跟踪优化的构建将使您四处奔走,并且通常无法直接检查变量,因为它们只会在寄存器中结束或完全被优化掉.这是一次"有趣"的体验......


尝试gcc为什么-g -O3并在调试版本中获得优化.xlC具有相同的行为.

5> 0124816..:

正如Greg所提到的,编译器不会在调试模式下执行此操作.调试版本比prod版本更慢,但它们不应该更频繁地崩溃:如果你依赖于尾部调用优化,它们可能就是这样做的.因此,通常最好将尾调用重写为普通循环.:-(

推荐阅读
php
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有