我只是比较将字符串传递给函数的性能。 benchmark results很有趣。
这是我的代码:
void add(std::string msg)
{
msg += "world";
}
void addRvalue(std::string&& msg)
{
msg += "world";
}
void addRef(std::string& msg)
{
msg += "world";
}
void StringCreation() {
add(std::string("hello "));
}
void StringCopy() {
std::string msg("hello ");
add(msg);
}
void StringMove() {
std::string msg("hello ");
add(std::move(msg));
}
void StringRvalue() {
std::string msg("hello ");
addRvalue(std::move(msg));
}
void StringReference() {
std::string msg("hello ");
addRef(msg);
}
StringCreation(),StringRvalue()和StringReference()是等效的。我很惊讶StringMove()性能最差-比涉及复制的值传递差。
我是否正确地认为调用StringMove()涉及一个move构造函数,然后在调用add()时涉及一个复制构造函数?它不仅仅涉及一举一动的构造函数吗?我认为移动构造对于字符串来说很便宜。
我增加了传递给add()的字符串的长度,这确实有所作为。现在,StringMove()仅比StringCreation和StringReference慢1.1倍。现在,StringCopy是最糟糕的,这正是我所期望的。
这是新的benchmark results。
因此,StringMove根本不涉及复制-仅适用于小字符串。
答案 0 :(得分:5)
让我们分析您的代码并假设使用长字符串(未应用SSO):
void add(std::string msg) {
msg += "world";
}
void StringCreation() {
add(std::string("hello "));
}
在这里,首先调用字符串文字中的转换构造函数( ConvC ),以初始化临时std::string("hello ")
。然后,该临时值(右值)由移动构造函数( MC )用于初始化参数msg
。但是,后者很可能通过 copy Elision 进行了优化。最后,调用运算符+=
。底线: 1x ConvC和1x +=
。
void StringCopy() {
std::string msg("hello ");
add(msg);
}
在这里,参数msg
由 lvalue 参数{{通过复制构造函数- CC )复制初始化。 1}}。底线: 1x ConvC,1x CC和1x msg
。对于长字符串,这是最慢的版本,因为复制涉及动态内存分配(唯一的情况)。
+=
为什么这比void StringMove() {
std::string msg("hello ");
add(std::move(msg));
}
慢?仅仅是因为有一个附加的MC可以初始化参数StringCreation
。由于对象msg
在调用msg
之后仍然存在,因此不能忽略它。只是它是从。底线: 1x ConvC,1x MC,1x add
。
+=
在这里,运算符void addRef(std::string& msg) {
msg += "world";
}
void StringReference() {
std::string msg("hello ");
addRef(msg);
}
应用于引用的对象,因此没有理由进行任何复制/移动。底线: 1x ConvC,1x +=
。与+=
的时间相同。
StringCreation
使用Clang时,时间与void addRvalue(std::string&& msg) {
msg += "world";
}
void StringRvalue() {
std::string msg("hello ");
addRvalue(std::move(msg));
}
相同。使用GCC,时间与StringReference
相同。实际上,我目前没有这种行为的解释。 (在我看来,GCC正在创建由 MC 初始化的其他一些临时文件。但是,我不知道为什么。)
答案 1 :(得分:0)
在此示例中,没有任何被“基准化”的功能实际上在起作用。也就是说,它们都没有实际返回计算值,然后将其用于其他地方。
因此,任何(半)像样的编译器都可能会决定完全忽略它们!
为了建立有效的基准,每次调用的结果字符串必须必须用于某些内容,甚至是简单输出到文件/控制台。
尝试以下代码以查看发生了什么(不是):
#include<iostream>
#include<string>
using namespace std;
void add(std::string msg)
{
msg += " + 'add'";
}
void addRef(std::string& msg)
{
msg += " + 'addRef'";
}
void addRvalue(std::string&& msg)
{
msg += " + 'addRefRef'";
}
int main()
{
std::string msg("Initial string!");
cout << msg << endl;
add(msg);
cout << msg << endl; // msg will be the same as before!
addRef(msg);
cout << msg << endl; // msg will be extended!
addRvalue(std::move(msg));
cout << msg << endl; // msg will again be extended
add(std::move(msg));
cout << msg << endl; // msg will be completely emptied!
return 0;
}