std::string 拼接操作的性能分析

C++ 标准库中提供的 std::string 类型有一些坑, 不小心使用的话很可能对程序性能造成影响. 例如字符串拼接这一常见简单的操作, 在 C++ 中大约有 4 种方式实现:

  1. + 操作符
  2. += 操作符
  3. append 成员
  4. stringstream<< 操作符

简单比较了下以上四种操作的性能, 初步结论是性能上 append $\approx$ += > + > stringstream

下面是测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <assert.h>
#include <chrono>
#include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>

template <typename Functor, typename... Args,
typename = decltype(std::declval<Functor>()(std::declval<Args>()...))>
void testWrapper(std::string_view name, Functor &&f, Args &&...args) {
const auto beginTime{std::chrono::steady_clock::now()};
std::forward<Functor>(f)(std::forward<Args>(args)...);
const auto endTime{std::chrono::steady_clock::now()};
std::cout << "test " << name << " runs for "
<< std::chrono::duration_cast<std::chrono::microseconds>(endTime -
beginTime)
.count()
<< " us\n";
}

std::string testStringAdd(const std::string &param1) {
std::string str = param1 + "111" + "222" + "333" + "444" + "555" + "666" +
"777" + "888" + "999" + "000";
return str;
}

std::string testStringAddEqual(const std::string &param1) {
std::string str = param1;
str += "111";
str += "222";
str += "333";
str += "444";
str += "555";
str += "666";
str += "777";
str += "888";
str += "999";
str += "000";
return str;
}

std::string testStringAppend(const std::string &param1) {
std::string str = param1;
str.append("111");
str.append("222");
str.append("333");
str.append("444");
str.append("555");
str.append("666");
str.append("777");
str.append("888");
str.append("999");
str.append("000");
return str;
}

std::string testStringStream(const std::string &param1) {
std::stringstream ss;
ss << param1 << "111"
<< "222"
<< "333"
<< "444"
<< "555"
<< "666"
<< "777"
<< "888"
<< "999"
<< "000";
return ss.str();
}

int main() {
const auto param = "test";
const auto str{testStringAdd(param)};
const auto run_test = [&str](size_t n, auto f, const std::string &s) {
for (size_t i{}; i < n; ++i) {
assert(f(s) == str);
}
};

constexpr size_t cnt{1000000};
testWrapper("add", run_test, cnt, testStringAdd, param);
testWrapper("add equal", run_test, cnt, testStringAddEqual, param);
testWrapper("append", run_test, cnt, testStringAppend, param);
testWrapper("string stream", run_test, cnt, testStringStream, param);
}

也可以在 compiler explorer 上查看: https://godbolt.org/z/cMPddcrfq

至于具体的分析以后有空再补…