VS2010 C++ 尾调用优化

10

考虑以下代码:

int fac_aux( int x, int res ) {
    if( x == 1 ) return res;
    else return fac_aux( x - 1, res * x );
}

int fac( int x ) {
    return fac_aux( x, 1 );
}

int main() {
    int x = fac( 50 );

    std::cout << x;
    return 0;
}
根据生成的汇编文件,一切正常,尾调用已经被优化。
尝试替换。
int x = fac( 50 );

使用

int x = fac_aux( 50, 1 );

奇怪的是,尾调用优化消失了。据我记得,在VS2008中没有这样奇怪的编译器行为。有什么想法为什么会发生这些事情,如何确保进行了尾调用优化?

; 函数编译标志:/Ogtp

尝试了/O2和/Ox两种优化标志。是否还有其他编译器选项很重要?

编辑:VS2012可以进行优化


当您修改代码时,是否删除了 fac 函数? - SirDarius
那么,没有被优化的函数实际上从未被调用?还是你在谈论第一个函数中的尾递归? - Euqil
@Voivoid:这个函数实际上在汇编中被“调用”了吗?(很可能是的,但还是问一下) - Mooing Duck
仅供参考,没有理由优先使用/Ox优化开关而不是/O2/Ox的名称“完全优化”有些具有误导性(至少在当代版本中); 实际上,您可以通过/O2获得至少与/Ox相同的优化水平,甚至更高。 - Cody Gray
5个回答

1

我尝试了以下代码

#include "stdafx.h"

int f( size_t i, int x )
{
    return ( ( i < 2 ) ? x : f( i - 1, i * x ) );
}

int f( size_t i )
{
    return ( f( i, 1 ) );
}

int _tmain(int argc, _TCHAR* argv[])
{
    {
        f( 0 );
    }

    return 0;
}

我使用了完整的优化/Ox,但是并没有得到尾递归。因此看来,MS VC++ 2010不支持尾递归。


1

当原始代码被编译时,调用点处的汇编代码会对fac_aux进行部分内联,特别是x - 1部分,这是尾递归所必需的,但使用fac_aux会阻止部分内联,从而阻止尾递归优化:

TestThin.fac_aux 013B1000   CMP ECX,1
013B1003                    JE SHORT TestThin.013B100E
013B1005                    IMUL EAX,ECX
013B1008                    DEC ECX
013B1009                    CMP ECX,1
013B100C                    JNZ SHORT TestThin.013B1005
013B100E                    RETN
013B100F                    INT3
TestThin.main 013B1010      MOV EAX,32
013B1015                    LEA ECX,DWORD PTR DS:[EAX-1] ;notice the partial inlining of x - 1
013B1018                    CALL TestThin.fac_aux

0

尝试将函数明确地设置为inline - 此外,您正在使用什么优化级别?


很遗憾,inline并没有起到帮助作用。已尝试使用/O2和/Ox标志。 - Voivoid

0

我不确定它是否有效,但尝试使用单个返回语句替换 if ... else:

return (x == 1) ? res : fac_aux( x - 1, res * x );

0

看起来很奇怪,你在做某种增量编译吗? 除此之外,编译器可能会因为多个参数而感到困惑,在工作版本中实际上只有一个参数,不知何故优化不再合格。

你可以尝试将res参数设置为全局变量,我知道这是一种混乱的坏习惯,但它可能有效。

听起来像是编译器的错误/特性。

/Tony


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接