繁体   English   中英

按值,引用和右值传递字符串

[英]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);
}

这里,参数msglvalue参数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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM