递归函数严重降低性能

Recursive Function Seriously slowing performance?

本文关键字:性能 递归函数      更新时间:2023-10-16

我有一个函数,我调用了无限多次(或者直到满足条件为止)。这个递归函数的问题是,在更高的级别上,它是由一个工作线程调用的,该线程推送并弹出到deque。与此同时,我正在用主线程迭代一个文件,这比递归函数快得多,递归函数处理通过主循环迭代获得的数据。下面是一个严重减慢速度的代码示例:

bool CCapsule::DecodeElementList( ElementList &elementList, char* pszBuffer, unsigned int uiBufferLength, std::string strSrcAddress, std::string strDestAddress, std::vector<std::string> &vsContents, bool bIsInner )
{
    // Create an buffer to contain pszBuffer.
    RBuffer rBuffer;
    rBuffer.data = pszBuffer;
    // Create an RElement List and Element Entry to store elements
    RRet rRet;
    RElementEntry rElementEntry;
    // Decode Element List
    if(rDecodeElementList(&m_rDecodeIter, &rElementList, 0) >= RET_SUCCESS)
    {
        std::vector<std::string> _vsContents;
        while ((RRet = rDecodeElementEntry(&m_rDecodeIter, &rElementEntry)) != RRET_END_OF_CONTAINER)
        {
            if (RRet < RET_SUCCESS)
            {
                return false;
            }
            std::string strEntryName = "";
            if (rElementEntry.data != NULL)
                rElementEntry.data;
            switch(rElementEntry.dataType)
            {
            case R_MSG:
                // Create a new RCapsule to encapsulate the inner message.
                m_pInnerMessage = new CCapsule();
                if ( false == m_pInnerMessage->Load( pszBuffer, uiBufferLength, strSrcAddress, strDestAddress, _vsContents, true ) )
                {
                    // An error occurred, clean up.
                    delete m_pInnerMessage;
                    m_pInnerMessage = 0;
                    return false;
                }
                break;
            case R_ELEMENT_LIST:
                // Decode Element List
                RElementList rInnerElementList;
                DecodeElementList(rInnerElementList, pszBuffer, uiBufferLength, strSrcAddress, strDestAddress, _vsContents, true);
                break;
            case R_FIELD_LIST:
                // Decode Field List
                DecodeFieldList(pszBuffer, uiBufferLength);
                break;
            case R_DATE:
                {
                    // Decode DATE
                    RDate rDate;
                    RRet = rDecodeDate( &m_rDecodeIter, &rDate );
                    if ( RRet != RET_SUCCESS )
                    {
                        return false;
                    }
                    std::stringstream sstream;
                    sstream << static_cast<int>( rDate.day ) << "/" << static_cast<int>( rDate.month ) << "/" << rDate.year;
                    _vsContents.push_back(sstream.str());
                }
                break;
            case R_DATETIME:
                {
                    // Decode DATETIME
                    RDateTime rDateTime;
                    RRet = rDecodeDateTime( &m_rDecodeIter, &rDateTime );
                    if ( RRet != RET_SUCCESS )
                    {
                        return false;
                    }
                    RBuffer rStringBuffer;
                    RRet = rDateTimeToString( &rStringBuffer, R_DATETIME,  &rDateTime );
                    if ( RRet != RET_SUCCESS )
                    {
                        return false;
                    }
                    std::stringstream sstream;
                    sstream << static_cast<int>( rDateTime.date.day ) << "/" << static_cast<int>( rDateTime.date.month ) << "/" << static_cast<int>( rDateTime.date.year) << " " << static_cast<int>( rDateTime.time.hour )
                        << ":" << static_cast<int>( rDateTime.time.minute ) << ":" << static_cast<int>( rDateTime.time.second ) << "." << static_cast<int>( rDateTime.time.millisecond ); 
                    _vsContents.push_back(sstream.str());
                }
                break;
            case R_DOUBLE:
                // Decode DOUBLE
                RDouble rDouble;
                RRet = rDecodeDouble( &m_rDecodeIter, &rDouble );
                _vsContents.push_back(boost::lexical_cast<std::string>(rDouble));
                //m_sStringStream << rDouble << ",";
                break;
            case R_UINT:
                // Decode UINT
                RUInt rUInt;
                RRet = rDecodeUInt( &m_rDecodeIter, &rUInt );
                _vsContents.push_back(boost::lexical_cast<std::string>(rUInt));
                //m_sStringStream << rUInt << ",";
                break;
            case R_ASCII_STRING:
                {
                // Decode STRING
                RBuffer rStringBuffer;
                RRet = rDecodeBuffer( &m_rDecodeIter, &rStringBuffer );
                std::string strData(rStringBuffer.data);
                _vsContents.push_back(strData);
                //m_sStringStream << rStringBuffer.data << ",";
                }
                break;
            case R_NO_DATA:
                RRet = RET_SUCCESS;
                break;
            default:
                RRet = RET_FAILURE;
                break;
            }
        }
        std::stringstream ssReport;
        std::copy(_vsContents.cbegin(),_vsContents.cend(),std::ostream_iterator<std::string>(ssReport,","));
        vsContents.push_back(ssReport.str());
    }
    else
    {
        return false;
    }
    return true;
}

不幸的是,它必须按照这个顺序,因为存储的字符串向量将包含逗号分隔的元素列表,稍后我将输出到csv列表。这段代码只需解码某些元素,并将生成的字符串推回到字符串向量中,然后将其放入字符串流中。

有人对如何提高性能有什么建议吗???谢谢

递归本身不会对性能产生影响。您的性能问题将是更传统的类型,例如:

  • 使用探查器来衡量性能。如果您将事情拆分为多个函数(例如,在这里,您读入的每个数据类型一个),这会更容易
  • 请验证(使用探查器或其他计时器)问题确实存在于您发布的代码中,而不是它调用的代码中。我们看不到其中一半的函数,很可能是它们占用了大部分处理时间,从这个函数中被重复调用
  • 更喜欢重用旧对象而不是创建新对象,更喜欢堆栈分配的对象而不是堆分配的对象,因为内存管理成本很高
  • 在填充std::vectors之前,请在其中保留空间,以减少在向其添加项时所做的重新分配和复制量
  • 在使用对象的范围内初始化对象(例如,将rNet移到if语句中),以避免为初始化从未使用过的对象付出代价
  • 在检查性能之前,请先检查错误。像if (rElementEntry.data != NULL) rElementEntry.data;这样的非抽象语句不会有任何用处,但它们可能会占用CPU周期
  • 用类似sprintf的更快的方法替换字符串格式化的std::stringstream。是的,我知道它更危险,但它几乎总是更快。只要小心,使用更安全的版本,如snprintf或您的平台等效版本。对不起,lexical_cast可能也是如此
  • 如果您绝对必须使用std::stringstream,请改用std::ostringstream——您不需要istream部分,对吧

您可以尝试用迭代算法替换递归算法,方法是将通常传递给递归函数的参数推送到堆栈中。

类似:

Stack<Object> stack = new Stack<Object>;
stack.push(first_object);
while( !stack.isEmpty() ) {
   // Do something
     my_object = stack.pop();
  // Push other objects on the stack.
}

这种技术被称为递归消除。请查看此链接。

http://cs.saddleback.edu/rwatkins/CS2B/Lab%20Exercises/Stacks%20and%20Recursion%20Lab.pdf

我觉得您的代码中有两件事很重要:

  1. 您的算法看起来像是图形中的DFS或n分支树中的预购旅行。因此,应该有一种简单的方法以迭代的方式来完成它。对于图或树中的每个节点,可以为其定义一个单独的Access()方法,并使代码更清晰
  2. 远离流,按照@Kylotan的建议使用snprintf

上面的更改可以使您的代码比以前更快。如果它仍然不能满足您的标准,那么您需要尝试一些分析工具。