其中,如果有,C++;编译器是否进行尾部递归优化? 在我看来,在C和C++中做尾递归优化都是很好的,然而在调试的时候,我似乎从来没有看到一个表示这个优化的帧堆栈。这有点好,因为堆栈告诉我递归有多深。不过,优化也会很好 是否有任何C++编译器进行了这种优化?为什么?为什么不呢
我该如何告诉编译器去做呢其中,如果有,C++;编译器是否进行尾部递归优化? 在我看来,在C和C++中做尾递归优化都是很好的,然而在调试的时候,我似乎从来没有看到一个表示这个优化的帧堆栈。这有点好,因为堆栈告诉我递归有多深。不过,优化也会很好 是否有任何C++编译器进行了这种优化?为什么?为什么不呢,c++,optimization,tail-recursion,C++,Optimization,Tail Recursion,我该如何告诉编译器去做呢 对于MSVC:/O2或/Ox 对于GCC:-O2或-O3 检查编译器是否在特定情况下执行了此操作,如何 对于MSVC,启用PDB输出以能够跟踪代码,然后检查代码 对于GCC 对于如何确定编译器是否对某个函数进行了这样的优化,我仍然会接受一些建议(尽管康拉德让我假设它,这让我感到放心) 通过执行无限递归并检查是否会导致无限循环或堆栈溢出(我使用GCC执行此操作并发现-O2足够了),始终可以检查编译器是否执行此操作,但我希望能够检查我知道将终止的某个函数。我希望有一
- 对于MSVC:
或/O2
/Ox
- 对于GCC:
或-O2
-O3
- 对于MSVC,启用PDB输出以能够跟踪代码,然后检查代码
- 对于GCC
-O2
足够了),始终可以检查编译器是否执行此操作,但我希望能够检查我知道将终止的某个函数。我希望有一个简单的方法来检查这个:)
经过一些测试,我发现析构函数破坏了进行这种优化的可能性。有时,更改某些变量和临时变量的范围以确保它们在return语句开始之前超出范围是值得的
如果在尾部调用后需要运行任何析构函数,则无法进行尾部调用优化。所有当前主流编译器都能很好地执行尾部调用优化(并且已经进行了十多年),例如: 让编译器进行优化很简单:只需打开速度优化:
- 对于MSVC,使用
或/O2
/Ox
- 对于GCC、Clang和ICC,使用
-O3
作为一个有趣的历史记录,Mark Probst在一次测试过程中将C的尾部调用优化添加到GCC中。本文描述了实现中一些有趣的注意事项。这本书值得一读。大多数编译器在调试版本中不进行任何类型的优化
如果使用VC,请尝试打开PDB信息的发布版本-这将让您跟踪优化后的应用程序,您应该希望看到您想要什么。然而,请注意,调试和跟踪优化的构建将使您四处奔波,并且通常您无法直接检查变量,因为它们只会在寄存器中结束或完全优化。这是一种“有趣”的体验…正如格雷格所提到的,编译器不会在调试模式下进行。调试构建比prod构建慢是可以的,但它们不应该更频繁地崩溃:如果您依赖于尾部调用优化,它们可能会做到这一点。因此,通常最好将尾部调用重写为普通循环:-(GCC4.3.2将此函数(蹩脚/琐碎的
atoi()
实现)完全内联到main()
中。优化级别是-O1
。我注意到如果我使用它(即使将其从静态
更改为外部
,尾部递归也会很快消失,因此我不会依赖它来保证程序的正确性
#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;
}
#包括
静态整数原子(常量字符*str,整数n)
{
如果(str==0 | |*str==0)
返回n;
返回原子(str+1,n*10+*str-'0');
}
int main(int argc,字符**argv)
{
对于(int i=1;i!=argc;++i)
printf(“%s->%d\n”,argv[i],atoi(argv[i],0));
返回0;
}
除了显而易见的(除非您要求,否则编译器不会进行此类优化)之外,C++中的尾部调用优化也很复杂:析构函数
假设:
int fn(int j, int i)
{
if (i <= 0) return j;
Funky cls(j,i);
return fn(j, i-1);
}
intfn(intj,inti)
{
如果(我相信ICC会这么做。据我所知,ICC生产的代码是市场上最快的。@保罗问题是,ICC代码的速度有多少是由算法优化(如尾部调用优化)引起的,有多少是由缓存和微指令优化引起的,这只有英特尔自己知道他们自己的处理器可以这样做。gcc
有更窄的选项-fooptimize sibling calls
“优化sibling and tail recursive calls”。此选项(根据针对不同平台的4.4、4.7和4.8版的gcc(1)
手册页)在级别-O2
,-O3
,-Os
上启用。此外,在调试模式下运行而不明确请求优化将不会进行任何优化。您可以为真正的发布模式EXE启用PDB,并尝试单步执行,但请注意,在发布模式下调试有其复杂性-不可见/剥离变量ABLE、合并变量、变量在未知/意外范围内超出范围、变量永远不会进入范围并成为具有堆栈级地址的真常量,以及-合并良好或缺少堆栈帧。通常合并堆栈帧表示被调用方是内联的,缺少/后向合并的帧可能是尾部调用。您可以激活链接时间虽然是优化,但我猜即使是extern
方法也可能是内联的。奇怪。我刚刚测试了GCC4.2.3(x86,Slackware 12.1)和GCC4.6.2(AMD64,Debian wheezy),并且使用-O1
没有内联和尾部递归优化。你必须使用-O2
(好吧,在4.2.x中,它现在已经相当古老了,它仍然不会被内联)
int fn(int j, int i)
{
if (i <= 0) return j;
Funky cls(j,i);
return fn(j, i-1);
}