2012-07-09 6 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; 
} 

인코딩과 특수 문자를 대체 할 완전히 대체하기() 코드 형태. 아래에 나와 있습니다.

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 항목을 인코딩했습니다. 그러나 실제 상황에서는 이것이 더 많을 수 있습니다. 위의 코드는 XML을 작성하는 데 약 20 초가 걸렸습니다. 이는 허용되는 한도를 훨씬 넘어 섰습니다. 무엇이 문제 일 수 있습니까? 여기 성능을 향상시키는 방법은 무엇입니까?

참고 : 문자열 클래스를 사용하더라도 큰 차이는 없습니다. MFC CString에서 또 다른 문자열 구현을 사용하여 동일한 로직을 테스트했으며 비슷한 (훨씬 나쁜) 관찰 결과를 얻었다. 또한 XML을 더 나은 방법으로 준비하기 위해 여기에 DOM XML 파서를 사용하고 싶지 않습니다. 이 질문은 XML과 관련이 없습니다.

+0

실행 한 프로파일 러의 출력은 무엇입니까? 병목 현상이 정확히 어디를 가리 킵니까? 배당? 데이터 사본? – PlasmaHH

+0

@PlasmaHH : 프로파일 러를 사용하지 않았지만, 함수 발생 시간대에서 각 항목 추가에 시간이 걸릴 것이라고 결론을 내릴 수있었습니다. 아래 답변을 참조하십시오. 아래 수정을 통해 성능을 획기적으로 향상시킬 수있었습니다. – PermanentGuest

답변

0

콘텐츠를 만들기 전에 결과 문자열 길이 (anXMLString)를 예측할 수 있으면 문자열에 충분한 버퍼 공간을 할당 할 수 있습니다. 버퍼가 충분히 크면 대상 문자열의 재 할당 및 복사가 발생하지 않습니다.

이 방법 :

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

나는 표준 : : 문자열에 대해 확실하지 않다, 그것은 점을 추가 검색해야하거나 메모리에 문자열의 킵 길이하지 않습니다.

0

같은 문자열이 길어지고 길어지고 그 결과가 다음과 같이 나타납니다. 1. 문자열 연결이 늘어남에 따라 문자열 연결이 더 비쌉니다. 2. 문자 교체 루프가 진행되고 점점 더 느려지면서 커다란 현에서 발생합니다.

이 문제를 해결하기 위해 임시 문자열을 사용하여 개별 항목 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는 Q & A와 관련하여 경험을 공유 할 수있는 옵션을 제공했기 때문에이를 활용하려고 생각했습니다. 모든 의견/개선을 환영합니다.

+0

잘 모르겠습니다 만, 많은 문자열 연결을 계획하면 문자열의 + 연산자 대신 stringstream을 사용하는 것이 더 효과적이라고 생각합니다. 그러나 차이점을 확인하기위한 테스트를 한 적이 없었습니다. –

+0

@ W.Goeman 분명히 각각의 구현에 달려 있지만, 익숙한'stringstream' 구현은'std :: string'에 추가함으로써 실제로 삽입되므로 성능이 향상되지 않습니다. –

+0

_might_를 개선하는 또 다른 방법은'std :: string'보다는'std :: vector '을 사용하는 것입니다. 'std :: vector'는'push_back'가 일정한 복잡성을 상각하기 위해서 지수 적 성장 패턴을 필요로합니다; 'std :: string'의 많은 초기 구현은 선형 성장을 사용했으며, 문자열의 크기가 매우 커지면 매우 느려졌습니다. (내가 말했듯이, 이것은 _ 차이가 있습니다. 그렇지 않을 수도 있습니다 : 현재 문자열 구현에서 어떤 일이 발생하는지 모르겠습니다.) –