[英]Passing a string by value, reference and rvalue
我只是比较将字符串传递给函数的性能。 基准测试结果很有意思。
这是我的代码:
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现在是最糟糕的,这是我的预期。
以下是新的基准测试结果 。
所以StringMove毕竟不涉及复制 - 只针对小字符串。
让我们分析你的代码并假设长字符串(没有应用SSO):
void add(std::string msg) {
msg += "world";
}
void StringCreation() {
add(std::string("hello "));
}
这里,首先调用字符串文字中的转换构造函数 ( ConvC )来初始化临时std::string("hello ")
。 然后使用此临时(rvalue)通过移动构造函数 ( MC )初始化参数msg
。 然而,后者很可能通过复制省略来优化。 最后,调用operator +=
。 底线: 1x ConvC和1x +=
。
void StringCopy() {
std::string msg("hello ");
add(msg);
}
这里,参数msg
由lvalue参数msg
复制初始化(通过复制构造函数 -CC )。 底线: 1x ConvC,1x CC和1x +=
。 在长字符串的情况下,这是最慢的版本,因为复制涉及动态内存分配(唯一的情况)。
void StringMove() {
std::string msg("hello ");
add(std::move(msg));
}
为什么这比StringCreation
慢? 只是因为有一个额外的MC参与初始化参数msg
。 它不能被省略,因为在调用add
之后对象msg
仍然存在。 只是它被移动了。 底线: 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初始化的临时临时。但是,我不知道为什么。)
在这个例子中,没有任何被“基准”的功能实际上做了任何后果。 也就是说,它们都没有实际返回计算值,然后在其他地方使用。
所以,任何(半)体面的编译器可能只会决定完全忽略它们!
为了进行有效的基准测试,每个/每个调用的字符串结果必须用于某些东西,甚至是简单的输出到文件/控制台。
试试这段代码,看看发生了什么(没有):
#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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.