2014-04-23 70 views
1

性能方面,現代C++編譯器中的以下函數之間是否有區別?std字符串連接性能

std::string ConcatA(const std::string& a, const std::string& b, const std::string& c) 
{ 
    return a + b + c; 
} 

std::string ConcatB(const std::string& a, const std::string& b, const std::string& c) 
{ 
    std::string r = a; 
    r += b; 
    r += c; 
    return r; 
} 
+3

如何循環兩個單獨的10,000次並比較差異? –

+0

儘管'ConcatA'構造了多個字符串,但它可能比'ConcatB'更快,因爲第二個字段可能會有額外的重新分配和複製。 –

+0

當產生這樣的連接時,源範圍中的「連接視圖」可以提供體面的性能優勢(甚至可以避免創建連接字符串)。這種連接的一個例子是'boost :: range :: join'(http://www.boost.org/doc/libs/1_55_0/libs/range/doc/html/range/reference/utilities/join.html ;它只需要2個源代碼範圍,但可以用C++ 11進行真正的多範圍連接。 – oakad

回答

1

ConcatB有1個臨時字符串,而ConcatA有2個臨時字符串,因此ConcatB快兩倍。

$貓cata.cpp

#include <string> 
#include <iostream> 
std::string ConcatA(const std::string& a, const std::string& b, const std::string& c) 
{ 
    return a + b + c; 
} 
int main(){ 
    std::string aa="aa"; 
    std::string bb="bb"; 
    std::string cc="cc"; 
    int count = 0; 
    for(int ii = 0; ii < 10000000; ++ii) { 
    count += ConcatA(aa, bb, cc).size(); 
    } 
    std::cout<< count <<std::endl; 
} 

$貓catb.cpp

#include <string> 
#include <iostream> 
std::string ConcatB(const std::string& a, const std::string& b, const std::string& c) 
{ 
    std::string r = a; 
    r += b; 
    r += c; 
    return r; 
} 
int main(){ 
    std::string aa="aa"; 
    std::string bb="bb"; 
    std::string cc="cc"; 
    int count = 0; 
    for(int ii = 0; ii < 10000000; ++ii) { 
    count += ConcatB(aa, bb, cc).size(); 
    } 
    std::cout<< count <<std::endl; 
} 

$鐺++ -v

Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn) 
Target: x86_64-apple-darwin13.1.0 
Thread model: posix 

$ clang++ cata.cpp 
$ time ./a.out 

60000000 

real 0m1.122s 
user 0m1.118s 
sys 0m0.003s 

$ clang++ catb.cpp 
$ time ./a.out 
60000000 

real 0m0.599s 
user 0m0.596s 
sys 0m0.002s 
$ 
1

我使用MinGW(TDM)編譯它4.8 .1帶有選項-fdump-tree-optimized,不帶-O2

第一個做這樣

string tmp = a+b; // that mean create new string g, g += b, tmp = g (+dispose g) 
tmp += c; 
return tmp; // and dispose tmp 

的移動第二做另一種說法

string tmp = a; // just copy a to tmp 
tmp += b; 
tmp += c; 
return tmp; // and dispose tmp 

它看起來就像這樣

void * D.20477; 
    struct basic_string D.20179; 

    <bb 2>: 
    D.20179 = std::operator+<char, std::char_traits<char>, std::allocator<char> > (a_1(D), b_2(D)); [return slot optimization] 
    *_3(D) = std::operator+<char, std::char_traits<char>, std::allocator<char> > (&D.20179, c_4(D)); [return slot optimization] 

    <bb 3>: 

    <bb 4>: 
    std::basic_string<char>::~basic_string (&D.20179); 
    D.20179 ={v} {CLOBBER}; 

<L1>: 
    return _3(D); 

<L2>: 
    std::basic_string<char>::~basic_string (&D.20179); 
    _5 = __builtin_eh_pointer (1); 
    __builtin_unwind_resume (_5); 

void * D.20482; 
    struct string r [value-expr: *<retval>]; 

    <bb 2>: 
    std::basic_string<char>::basic_string (r_1(D), a_2(D)); 
    std::basic_string<char>::operator+= (r_1(D), b_3(D)); 

    <bb 3>: 
    std::basic_string<char>::operator+= (r_1(D), c_4(D)); 

    <bb 4>: 

<L0>: 
    return r_1(D); 

<L1>: 
    std::basic_string<char>::~basic_string (r_1(D)); 
    _5 = __builtin_eh_pointer (1); 
    __builtin_unwind_resume (_5); 

所以,應用-O2優化後n編譯器將ConcatB函數保持在幾乎相同的視圖中,並通過內聯函數,向內存分配部分添加常量值,聲明新函數,但最有價值的部分保持不變。

ConcatA:

D.20292 = std::operator+<char, std::char_traits<char>, std::allocator<char> > (a_2(D), b_3(D)); [return slot optimization] 
    *_5(D) = std::operator+<char, std::char_traits<char>, std::allocator<char> > (&D.20292, c_6(D)); 

ConcatB:

std::basic_string<char>::basic_string (r_3(D), a_4(D)); 
    std::basic_string<char>::append (r_3(D), b_6(D)); 
    std::basic_string<char>::append (r_3(D), c_8(D)); 

所以,很明顯,ConcatB比ConcatA更好,因爲它確實少分配操作,這是當你想非常昂貴優化這些小代碼。