2012-07-09 60 views
0

最近我们遇到了一段代码生成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。这远远超出了任何可接受的限度。可能是什么问题呢?如何提高这里的表现?

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

+0

什么是你运行了分析器的输出,正是它指向为瓶颈?分配?数据的副本? – PlasmaHH 2012-07-09 11:23:55

+0

@PlasmaHH:我没有使用任何分析器,只是从功能输入时间,我能够得出结论,每个项目增加需要时间。请参阅下面的答案。通过以下修改,我能够大幅提升性能。 – PermanentGuest 2012-07-09 13:14:41

回答

0

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

这样:

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

我不知道有关的std :: string,它需要搜索附加点,或者处于字符串的长度保持在内存中。

0

我意识到问题可能是由于一个事实,即相同的字符串是越来越长,这导致了以下内容:作为字符串增长 2.字符替换 1.字符串连接变得更加昂贵随着循环的进行而发生在更大的字符串中,并且变得越来越慢。

为了解决这个问题,我使用的临时字符串来获得编码的个别项目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毫秒!

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

由于计算器提供了一个分享Q中&一个方面的经验进行选择,我想利用它。欢迎任何意见/改进。

+0

我不确定,但我认为当你打算做很多字符串连接时,使用stringstream而不是字符串的+运算符会更高效。但我从来没有做过检查差异的测试。 – 2012-07-09 11:21:05

+0

@ W.Goeman它显然取决于每一个的实现,但是我熟悉的'stringstream'的实现通过附加到'std :: string'实际插入,所以它们不会提高性能。 – 2012-07-09 12:36:04

+0

另一件事_might_改进的东西是使用'std :: vector '而不是'std :: string'。 'std :: vector'需要指数增长模式,以便'push_back'具有分期不变的复杂性;很多'std :: string'的早期实现使用线性增长,并且在字符串变得非常大时变得非常缓慢。 (正如我所说,这个_might_有所作为,或者可能不会:我不知道当前字符串实现中发生了什么太多。) – 2012-07-09 12:39:46