我只是比较将字符串传递给函数的性能。 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()涉及一个移动构造函数,然后在调用add()时涉及一个复制构造函数,对吗?它不仅仅涉及一举一动的构造函数吗?我认为移动构造对于字符串来说是便宜的。
更新
我增加了传递给add()的字符串的长度,这确实有所作为。现在,StringMove()仅比StringCreation和StringReference慢1.1倍。 StringCopy现在是最糟糕的,这正是我所期望的。
这是新的benchmark results。
因此,StringMove根本不涉及复制-仅适用于小字符串。
最佳答案
让我们分析您的代码并假设使用长字符串(未应用SSO):
void add(std::string msg) {
msg += "world";
}
void StringCreation() {
add(std::string("hello "));
}
在这里,首先调用来自字符串文字的转换构造函数( ConvC )以初始化临时
std::string("hello ")
。然后,通过移动构造函数( MC )使用此临时值(一个右值)来初始化参数msg
。但是,后者很可能会通过复制删除进行优化。最后,调用运算符+=
。底线: 1x ConvC和1x +=
。void StringCopy() {
std::string msg("hello ");
add(msg);
}
在这里,参数
msg
由左值参数msg
复制初始化(通过复制构造函数- CC )。底线: 1x ConvC,1x CC和1x +=
。对于长字符串,这是最慢的版本,因为复制涉及动态内存分配(唯一的情况)。void StringMove() {
std::string msg("hello ");
add(std::move(msg));
}
为什么这比
StringCreation
慢?仅仅是因为有一个附加的MC会初始化参数msg
。不能忽略它,因为在调用msg
之后,对象add
仍然存在。只是它是从。底线: 1x ConvC,1x MC,1x +=
。void addRef(std::string& msg) {
msg += "world";
}
void StringReference() {
std::string msg("hello ");
addRef(msg);
}
此处,运算符
+=
应用于引用的对象,因此没有理由进行任何复制/移动。底线: 1x ConvC,1x +=
。与StringCreation
相同的时间。void addRvalue(std::string&& msg) {
msg += "world";
}
void StringRvalue() {
std::string msg("hello ");
addRvalue(std::move(msg));
}
使用Clang的时间与
StringReference
的时间相同。对于GCC,时间与StringMove
相同。实际上,我目前没有这种行为的解释。 (在我看来,GCC正在创建由 MC 初始化的其他一些临时文件。但是,我不知道为什么。)关于c++ - 通过值,引用和右值传递字符串,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57725463/