字符串操作性能问题

String manipulation performance problems

本文关键字:问题 性能 操作性 操作 字符串      更新时间:2023-10-16

最近,我们遇到了一段生成XML的代码的性能问题。想在这里分享经验。这有点长,请耐心等待。

我们准备了一个包含许多项的简单XML。每个项目可以有5-10个元素。结构是这样的:

<Root>
    <Item>
        <Element1Key>Element1Val</Element1Key>
        <Element2Key>Element2Val</Element2Key>
        <Element3Key>Element3Val</Element3Key>
        <Element4Key>Element4Val</Element4Key>
        <Element5Key>Element5Val</Element5Key>
    <Item>
    <Item>
        <Element1Key>Element1Val</Element1Key>
        <Element2Key>Element2Val</Element2Key>
        <Element3Key>Element3Val</Element3Key>
        <Element4Key>Element4Val</Element4Key>
        <Element5Key>Element5Val</Element5Key>
    <Item>
</Root>

生成XML的代码是(以全局函数的简化形式):

void addElement(std::string& aStr_inout, const std::string& aKey_in, const std::string& aValue_in)
{
    aStr_inout += "<";
    aStr_inout += aKey_in;
    aStr_inout += ">";
    aStr_inout += "Elemem1Val";
    aStr_inout += "<";
    aStr_inout += aValue_in;
    aStr_inout += ">";
}
void PrepareXML_Original()
{
    clock_t commence,complete;
    commence=clock();
    std::string anXMLString;
    anXMLString += "<Root>";
    for(int i = 0; i < 200; i++)
    {
        anXMLString += "<Item>";
        addElement(anXMLString, "Elemem1Key", "Elemem1Value");
        addElement(anXMLString, "Elemem2Key", "Elemem2Value");
        addElement(anXMLString, "Elemem3Key", "Elemem3Value");
        addElement(anXMLString, "Elemem4Key", "Elemem4Value");
        addElement(anXMLString, "Elemem5Key", "Elemem5Value");
        anXMLString += "</Item>";

        replaceAll(anXMLString, "&", "&amp;");
        replaceAll(anXMLString, "'", "&apos;");
        replaceAll(anXMLString, """, "&quot;");
        replaceAll(anXMLString, "<", "&lt;");
        replaceAll(anXMLString, ">", "&gt;");
    }
    anXMLString += "</Root>";
    complete=clock();
    LONG lTime=(complete-commence);
    std::cout << "Time taken for the operation is :"<< lTime << std::endl;
}

replaceAll()代码将用编码的形式替换特殊字符。如下所示。

void replaceAll(std::string& str, const std::string& from, const std::string& to) 
{
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) 
    {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length();
    }
}

在最小的例子中,我已经编码了200个项目。但是,在实际情况下,情况可能会更糟。上面的代码花了大约20秒创建XML。这远远超出了任何可接受的限度。可能是什么问题?如何提高这里的性能?

注意:字符串类的用法没有太大区别。我用MFC CString的另一个字符串实现测试了相同的逻辑,得到了类似的(更糟糕的)观察结果。此外,我不想在这里使用任何DOM XML解析器来更好地准备XML。这个问题并不是XML特有的。

如果您可以在创建内容之前估计结果字符串(anXMLString)的长度,那么您可以为字符串分配足够的缓冲空间。当缓冲区足够大时,就不会重新分配和复制(目标字符串)。

这样:

std::string anXMLString;
anXMLString.reserve( size );

我不确定std::string,它是否需要搜索附加点,或者是否在内存中保持字符串的长度。

我意识到问题可能是由于同一个字符串越来越长,结果如下:1.随着字符串的增长,字符串连接变得更加昂贵2.随着循环的进行,字符替换发生在较大的字符串中,并且速度越来越慢。

为了解决这个问题,我使用了一个临时字符串来对单个项目进行XML编码,在循环结束时,我将这个小XML附加到主XML中。修改后的方法如下。

for(int i = 0; i < 200; i++)
{
    std::string anItemString;  // Create a new string for the individual Item entry
    anItemString += "<Item>";
    addElement(anItemString, "Elemem1Key", "Elemem1Value");
    addElement(anItemString, "Elemem2Key", "Elemem2Value");
    addElement(anItemString, "Elemem3Key", "Elemem3Value");
    addElement(anItemString, "Elemem4Key", "Elemem4Value");
    addElement(anItemString, "Elemem5Key", "Elemem5Value");
    anItemString += "</Item>";

    replaceAll(anItemString, "&", "&amp;");
    replaceAll(anItemString, "'", "&apos;");
    replaceAll(anItemString, """, "&quot;");
    replaceAll(anItemString, "<", "&lt;");
    replaceAll(anItemString, ">", "&gt;");
    anXMLString += anItemString;   // Do all the operations on the new string and finally append to the main string.
}

这提高了XML创建的性能,所花费的时间仅为17毫秒!

因此,我学到的教训是,当创建更大的结果时,将其拆分为子运算,将子运算的结果收集到新的字符串中,并向全局结果追加一次。我不确定这是否已经是一个模式或名称。

由于StackOverFlow提供了在问答方面分享经验的选项;A、 我想利用它。欢迎任何意见/改进。