【问题标题】:How true is "Want Speed? Pass by value"“想要速度?按价值传递”有多真实
【发布时间】:2014-03-03 13:21:01
【问题描述】:

如果我错了,请纠正我。说我有:

struct X
{
    std::string mem_name;

    X(std::string name)
        : mem_name(std::move(name)) 
    {}
    ...
};
struct Y
{
    std::string mem_name;

    Y(const std::string &name)
        : mem_name(name) 
    {}
    ...
};

X 的ctor 中,name 显然是传递给X 的任何参数的副本,X 调用std::string 的移动ctor 来初始化mem_name,对吗?

我们称之为在X*上复制然后移动;两个操作:复制、移动

Y 的ctor 中,name 是一个常量引用,这意味着没有元素的实际副本,因为我们直接处理从需要创建Y 对象的任何位置传递的参数.但随后我们复制了name 以在Y 中初始化mem_name;一次操作:复制。因此,它当然应该更快(并且对我来说更可取)?

在 Scott Meyer 的 GN13 演讲中(大约在 8:10 和 8:56 的时间范围内),他谈到了“想要速度?通过价值传递”,我想知道是否存在任何性能差异或通过引用传递参数(或准确地说是字符串)和按值传递“以提高速度?”

我知道按值传递参数可能会很昂贵,尤其是在处理大数据时。

也许(很明显?)我在他的演讲中遗漏了什么?

【问题讨论】:

标签: c++ c++11


【解决方案1】:

"Want speed? Pass by value"(1) 的想法是有时可以省略副本。参加XY 的课程,请考虑以下用例:

// Simulating a complex operation returning a temporary:
std::string foo() { return "a" + std::string("b"); }


struct X
{
  std::string mem_name;
  X(std::string name): mem_name(std::move(name)) {}
};

struct Y
{
  std::string mem_name;
  Y(const std::string &name): mem_name(name) {}
};


int main()
{
  X(foo());
  Y(foo());
}

现在让我们来分析这两种构造案例。

X 首先。 foo() 返回一个临时值,用于初始化对象name。然后将该对象移至mem_name。请注意,编译器可以应用返回值优化,在name 的空间中直接构造foo() 的返回值(实际上甚至是operator+ 的返回值)。所以实际上不会发生复制,只是移动。

现在让我们分析Yfoo() 再次返回一个临时值,它绑定到引用 name。现在返回值没有“外部提供”的空间,所以它必须在自己的空间中构造并绑定到引用。然后将其复制到mem_name。所以我们正在做一个副本,没有办法绕过它。

简而言之,结果是:

    1234563此外,X 将执行移动(在初始化mem_name 时)。
  • 如果传入一个右值,X 可能只会执行移动,而Y 必须执行复制。

通常,移动应该是一个操作,其时间要求与传递指针的时间要求相当(这就是通过引用传递所做的)。所以实际上,X 对于左值并不比 Y 差,对于右值更好。

当然,这不是绝对的规则,必须持保留态度。如有疑问,请提供个人资料。


(1) 链接很容易暂时不可用,并且截至 2014 年 11 月 12 日,它似乎已损坏 (404)。内容的副本(尽管格式很奇怪)似乎可以在几个博客网站上找到:

或者,可以通过wayback machine 访问原始内容。

还请注意,该主题总体上引起了相当大的讨论。谷歌搜索论文标题带来了很多后续行动和反驳。要列出其中一个示例,SO 成员 juanchopanza 提供了 "Want speed? Don't (always) pass by value"

【讨论】:

【解决方案2】:

优化没有通用规则。通过值传递可以在 C++11 中通过移动语义和复制省略带来一些重大胜利。

如果你真的想要速度,Profile 你的代码。

【讨论】:

    【解决方案3】:

    如果您真的不介意在您的 API 中公开非引用(这应该表明您将在内部复制/分配给定对象),那么使用副本是可以的。

    复制省略比移动快,如果不能省略(由于各种原因,比如依赖函数调用的调用链太长),那么 C++ 保证移动语义。

    【讨论】:

    • +1 用于提及公共 API 中的效果(和影响)。
    猜你喜欢
    • 2016-09-13
    • 2021-01-16
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-05
    • 1970-01-01
    • 2017-02-22
    相关资源
    最近更新 更多