Performance 递归和非递归实现的速度比较

Performance 递归和非递归实现的速度比较,performance,caching,recursion,stack,heap,Performance,Caching,Recursion,Stack,Heap,我有一个非常复杂的算法,它使用非常深的递归。由于某些特定数据存在堆栈溢出,我尝试在不递归的情况下重写它(使用堆上的外部堆栈)。所以我对同一个算法做了两次修改。然后我进行了一些测试,发现递归实现比另一个实现要快得多 有人能给我解释一下吗?讨论这些结果是我最后一个大学项目的一部分(为什么一个实现比另一个实现快得多)。我认为这是因为堆栈和堆的缓存不同,但我不确定 非常感谢 编辑 好的,有一个密码。该算法用C++编写,解决了树同构问题。除了一种比较两个节点的方法外,这两种实现都是相同的。比较是递归定义

我有一个非常复杂的算法,它使用非常深的递归。由于某些特定数据存在堆栈溢出,我尝试在不递归的情况下重写它(使用堆上的外部堆栈)。所以我对同一个算法做了两次修改。然后我进行了一些测试,发现递归实现比另一个实现要快得多

有人能给我解释一下吗?讨论这些结果是我最后一个大学项目的一部分(为什么一个实现比另一个实现快得多)。我认为这是因为堆栈和堆的缓存不同,但我不确定

非常感谢


编辑

好的,有一个密码。该算法用C++编写,解决了树同构问题。除了一种比较两个节点的方法外,这两种实现都是相同的。比较是递归定义的——如果一个节点的子节点小于另一个节点的对应子节点,则一个节点小于另一个节点

递归版本

char compareTo( const IMisraNode * nodeA, const IMisraNode * nodeB ) const {
    // comparison of same number of children
    int min = std::min( nodeA->getDegree( ), nodeB->getDegree( ) );
    for ( int i = 0; i < min; ++i ) {
        char res = compareTo( nodeA->getChild( i ), nodeB->getChild( i ) );
        if ( res < 0 ) return -1;
        if ( res > 0 ) return 1;
    }
    if ( nodeA->getDegree( ) == nodeB->getDegree( ) ) return 0; // same number of children
    else if ( nodeA->getDegree( ) == min ) return -1;
    else return 1;
}
char compareTo(常数IMisraNode*nodeA,常数IMisraNode*nodeB)常数{
//相同数量儿童的比较
intmin=std::min(nodeA->getDegree(),nodeB->getDegree());
对于(int i=0;igetChild(i),nodeB->getChild(i));
如果(res<0)返回-1;
如果(res>0)返回1;
}
if(nodeA->getDegree()==nodeB->getDegree())返回0;//子节点数相同
否则如果(nodeA->getDegree()==min)返回-1;
否则返回1;
}
非递归实现

struct Comparison {
    const IMisraNode * nodeA;
    const IMisraNode * nodeB;
    int i;
    int min; // minimum of count of children

    Comparison( const IMisraNode * nodeA, const IMisraNode * nodeB ) :
    nodeA( nodeA ), nodeB( nodeB ),
    i( 0 ), min( std::min( nodeA->getDegree( ), nodeB->getDegree( ) ) ) { }
} ;

char compareTo( const IMisraNode * nodeA, const IMisraNode * nodeB ) const {
    Comparison * cmp = new Comparison( nodeA, nodeB );
    // stack on the heap
    std::stack<Comparison * > stack;
    stack.push( cmp );

    char result = 0; // result, the equality is assumed

    while ( !result && !stack.empty( ) ) { // while they are not same and there are nodes left
        cmp = stack.top( );

        // comparison of same children
        if ( cmp->i < cmp->min ) {
            // compare these children
            stack.push( new Comparison( cmp->nodeA->getChild( cmp->i ), cmp->nodeB->getChild( cmp->i ) ) );
            ++cmp->i; // next node
            continue; // continue in comparing on next level
        }
        if ( cmp->nodeA->getDegree( ) != cmp->nodeB->getDegree( ) ) { // count of children is not same
            if ( cmp->nodeA->getDegree( ) == cmp->min ) result = -1; // node A has lesser count of children
            else result = 1; 
        }
        delete cmp;
        stack.pop( );
    }

    while ( !stack.empty( ) ) { // clean stack
        delete stack.top( );
        stack.pop( );
    }

    return result;
}
结构比较{ 常数imisr阳极*nodeA; 常数IMISR阳极*节点B; int i; int min;//子项计数的最小值 比较(常数IMisraNode*nodeA,常数IMisraNode*nodeB): nodeA(nodeA),nodeB(nodeB), i(0),min(std::min(nodeA->getDegree(),nodeB->getDegree()){ } ; 字符比较(常数IMisraNode*nodeA,常数IMisraNode*nodeB)常数{ 比较*cmp=新的比较(节点A、节点B); //堆积如山 std::堆栈; 堆栈推送(cmp); char result=0;//result,假定相等 虽然(!result&&!stack.empty()){//,但它们不相同,并且还剩下一些节点 cmp=stack.top(); //相同儿童的比较 如果(cmp->imin){ //比较这些孩子 推送(新比较(cmp->nodeA->getChild(cmp->i),cmp->nodeB->getChild(cmp->i)); ++cmp->i;//下一个节点 continue;//继续在下一级别进行比较 } 如果(cmp->nodeA->getDegree()!=cmp->nodeB->getDegree()){//子级的计数不相同 如果(cmp->nodeA->getDegree()==cmp->min)结果=-1;//节点A的子节点数较少 其他结果=1; } 删除cmp; stack.pop(); } 而(!stack.empty()){//clean stack 删除stack.top(); stack.pop(); } 返回结果; }
非递归代码执行动态内存分配(显式使用new,隐式使用std::stack),而递归代码则不执行。动态内存分配是一项极其昂贵的操作

要加快速度,请尝试存储值,而不是指针:

stack <Comparison> astack;

这并没有回答您的速度比较问题,而是为您的递归解决方案提供了增加堆栈大小的方法

您可以在Visual Studio帮助中的VC++:搜索“stacksize”下增加堆栈大小(默认值:1MB)


在gcc下也可以这样做。在这个问题上有一个很好的答案

什么语言,什么实现?这就产生了巨大的差异。C++。区别只在于一种方法(递归和非递归实现)没有您试图实现的算法的细节,递归代码和非递归代码,很难给出任何有意义的答案。不过,我读过的大多数教科书都指出递归通常比迭代慢,所以迭代版本慢似乎很奇怪。尾部递归优化支持是一种语言功能,它可以显著影响递归版本的性能。尤其是当您遇到堆栈溢出问题时。@GordonM我编辑了问题OK,它证实了我的假设。但是,是all还是eg.cache具有重大影响?顺便问一下,在不使用动态内存来保持原始性能的情况下重写这种类型的递归有什么不同的解决方案吗?因为非递归版本的速度是动态分配的4-5倍,所以其他一切都变得微不足道。为了加快速度,您可以将自己的堆栈实现为一个固定大小的数组,只分配一次,并在其中存储比较对象,而不是比较指针。我不是一个C程序员,但对于几乎所有的编程来说,如果在循环中有一个昂贵的操作,那么将其移出循环将始终产生最大的性能收益。在这种情况下,需要在循环开始时分配一大块内存,而不是在循环内部分配多块内存。当然,在开始之前,您需要计算出内存块的大致大小,否则将浪费内存或耗尽内存(如递归版本中的堆栈溢出)@GordonM:或者您可以在每次溢出时将存储空间增加一倍。它具有与预分配正确大小相同的复杂性类O(n)。
astack.push( Comparison( cmp->nodeA->getChild( cmp->i ), cmp->nodeB->getChild( cmp->i ) ) );

Comparison cp = astack.top();