“要速度吗?”经过价值”

时间:2022-07-03 16:39:01

Correct me if I'm wrong. Say I have:

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

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) 
    {}
    ...
};

In X's ctor, name is obviously a copy of whatever argument got passed to X, X invokes the move ctor of std::string to initialize mem_name, right?

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

Let's call that a copy-then-move on X*; two operations: COPY, MOVE.

我们把它叫做复制然后移动X*;两个操作:复制,移动。

In Y's ctor, name is a const ref, which means there's no actual copy of the element because we're dealing directly with the argument passed from wherever Y's object needs to be created. But then we copied name to initialise mem_name in Y; one operation: COPY. Surely it should therefore be a lot faster (and preferable to me)?

在Y的ctor中,name是const ref,这意味着没有元素的实际拷贝,因为我们直接处理从Y的对象需要创建的任何地方传递的参数。然后我们复制名称来初始化Y中的mem_name;一个操作:复制。因此,它肯定应该快得多(而且比我更可取)?

In Scott Meyer's GN13 talk (around time-frame 8:10 and 8:56), he talks about "Want speed? Pass by value" and I was wondering is there any performance difference or loss in passing arguments (or strings to be precise) by reference and passing by value "in order to gain speed?"

在斯科特·迈耶(Scott Meyer)关于GN13的谈话(大约在时间框架8:10和8:56)中,他谈到了“想要速度吗?”“通过值传递”,我想知道通过引用传递参数(准确地说,是字符串)和通过值传递参数“以获得速度”是否存在性能差异或损失?

I'm aware of the fact that passing arguments by value can be expensive, especially when dealing with large data.

我意识到按值传递参数是非常昂贵的,特别是在处理大数据时。

Maybe (clearly?) there's something I'm missing from his talk?

也许(很明显?)他的演讲中有些东西我没听清楚?

3 个解决方案

#1


48  

The idea of "Want speed? Pass by value"(1) is that sometimes, the copy can be elided. Taking your classes X and Y, consider this usecase:

“想要速度?”通过值传递"(1)是指有时可以省略复制。考虑到你的课程X和Y,考虑一下这个usecase:

// 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());
}

Now let's analyse both the construction cases.

现在我们来分析这两个施工案例。

X first. foo() returns a temporary, which is used to initialise the object name. That object is then moved into mem_name. Notice that the compiler can apply Return Value Optimisation and construct the return value of foo() (actually even the return value of operator+) directly in the space of name. So no copying actually happens, only a move.

X。foo()返回一个临时的,用于初始化对象名。然后将该对象移动到mem_name中。注意,编译器可以应用返回值优化,并在名称空间中直接构造foo()的返回值(实际上甚至是操作符+的返回值)。所以实际上没有复制,只有一个动作。

Now let's analyse Y. foo() returns a temporary again, which is bound to the reference name. Now there's no "externally supplied" space for the return value, so it has to be constructed in its own space and bound to the reference. It is then copied into mem_name. So we are doing a copy, no way around it.

现在让我们分析一下。foo()返回一个临时的,它绑定到引用名称。现在返回值没有“外部提供”的空间,因此必须在自己的空间中构造并绑定到引用。然后将它复制到mem_name中。所以我们在复制,没有办法绕过它。

In short, the outcome is:

简而言之,结果是:

  • If an lvalue is being passed in, both X and Y will perform a copy (X when initialising name, Y when initialising mem_name). In addition, X will perform a move (when initialising mem_name).

    如果一个lvalue被传入,X和Y都将执行一个副本(初始化名称时X,初始化mem_name时Y)。此外,X将执行一个move(在初始化mem_name时)。

  • If an rvalue is being passed in, X will potentially only perform a move, while Y has to perform a copy.

    如果一个rvalue被传入,X可能只执行一个移动,而Y必须执行一个拷贝。

Generally, a move is expected to be an operation whose time requirements are comparable to those of passing a pointer (which is what passing by reference does). So in effect, X is no worse than Y for lvalues, and better for rvalues.

通常,一个移动被期望为一个操作,其时间需求与传递一个指针的操作相当(这是通过引用传递的操作)。所以实际上,对于lvalue X不比Y差,对于rvalue X也比Y好。

Of course, it's not an absolute rule, and must be taken with a grain of salt. When in doubt, profile.

当然,这不是绝对的规则,必须持保留态度。有疑问时,配置文件。


(1) The link is prone to being temporarily unavailable, and as of 11-12-2014, it seems broken (404). A copy of the contents (albeit with weird formatting) seems available at several blog sites:

(1)该链接很容易暂时无法使用,到2014年11月12日,该链接似乎已经失效(404)。一些博客网站似乎可以找到内容的拷贝(尽管格式很奇怪):

Alternatively, the original content might be accessible through the wayback machine.

或者,可以通过回线机访问原始内容。

Also note that the topic has in general stirred up quite a discussion. Googling the paper title brings up a lot of follow-ups and counter-points. To list an example of one of these, there's "Want speed? Don't (always) pass by value" by SO member juanchopanza

还要注意的是,这个话题已经引起了广泛的讨论。在谷歌上搜索论文标题,会有很多后续报道和反驳。举一个例子,“想要速度吗?”不要(总是)通过“价值”传递给juanchopanza这样的成员

#2


12  

There are no general rules for optmization. Pass-by-value can give some big wins in C++11 with move semantics, alongside copy elision.

选择没有通用的规则。在c++ 11中,除了复制省略之外,通过移动语义,按值传递可以获得一些重大的胜利。

If you really want speed, Profile your code.

如果您真的想要速度,请对代码进行概要分析。

#3


3  

If you really don't mind exposing non-references in your API (which SHOULD BE a sign that internally you will be copying/assigning given object) then using copies is ok.

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

Copy elision is faster than moving, and if it can't be elided (for various reasons, like too long call chain of dependent function calls), then C++ guarantees move semantics.

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

#1


48  

The idea of "Want speed? Pass by value"(1) is that sometimes, the copy can be elided. Taking your classes X and Y, consider this usecase:

“想要速度?”通过值传递"(1)是指有时可以省略复制。考虑到你的课程X和Y,考虑一下这个usecase:

// 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());
}

Now let's analyse both the construction cases.

现在我们来分析这两个施工案例。

X first. foo() returns a temporary, which is used to initialise the object name. That object is then moved into mem_name. Notice that the compiler can apply Return Value Optimisation and construct the return value of foo() (actually even the return value of operator+) directly in the space of name. So no copying actually happens, only a move.

X。foo()返回一个临时的,用于初始化对象名。然后将该对象移动到mem_name中。注意,编译器可以应用返回值优化,并在名称空间中直接构造foo()的返回值(实际上甚至是操作符+的返回值)。所以实际上没有复制,只有一个动作。

Now let's analyse Y. foo() returns a temporary again, which is bound to the reference name. Now there's no "externally supplied" space for the return value, so it has to be constructed in its own space and bound to the reference. It is then copied into mem_name. So we are doing a copy, no way around it.

现在让我们分析一下。foo()返回一个临时的,它绑定到引用名称。现在返回值没有“外部提供”的空间,因此必须在自己的空间中构造并绑定到引用。然后将它复制到mem_name中。所以我们在复制,没有办法绕过它。

In short, the outcome is:

简而言之,结果是:

  • If an lvalue is being passed in, both X and Y will perform a copy (X when initialising name, Y when initialising mem_name). In addition, X will perform a move (when initialising mem_name).

    如果一个lvalue被传入,X和Y都将执行一个副本(初始化名称时X,初始化mem_name时Y)。此外,X将执行一个move(在初始化mem_name时)。

  • If an rvalue is being passed in, X will potentially only perform a move, while Y has to perform a copy.

    如果一个rvalue被传入,X可能只执行一个移动,而Y必须执行一个拷贝。

Generally, a move is expected to be an operation whose time requirements are comparable to those of passing a pointer (which is what passing by reference does). So in effect, X is no worse than Y for lvalues, and better for rvalues.

通常,一个移动被期望为一个操作,其时间需求与传递一个指针的操作相当(这是通过引用传递的操作)。所以实际上,对于lvalue X不比Y差,对于rvalue X也比Y好。

Of course, it's not an absolute rule, and must be taken with a grain of salt. When in doubt, profile.

当然,这不是绝对的规则,必须持保留态度。有疑问时,配置文件。


(1) The link is prone to being temporarily unavailable, and as of 11-12-2014, it seems broken (404). A copy of the contents (albeit with weird formatting) seems available at several blog sites:

(1)该链接很容易暂时无法使用,到2014年11月12日,该链接似乎已经失效(404)。一些博客网站似乎可以找到内容的拷贝(尽管格式很奇怪):

Alternatively, the original content might be accessible through the wayback machine.

或者,可以通过回线机访问原始内容。

Also note that the topic has in general stirred up quite a discussion. Googling the paper title brings up a lot of follow-ups and counter-points. To list an example of one of these, there's "Want speed? Don't (always) pass by value" by SO member juanchopanza

还要注意的是,这个话题已经引起了广泛的讨论。在谷歌上搜索论文标题,会有很多后续报道和反驳。举一个例子,“想要速度吗?”不要(总是)通过“价值”传递给juanchopanza这样的成员

#2


12  

There are no general rules for optmization. Pass-by-value can give some big wins in C++11 with move semantics, alongside copy elision.

选择没有通用的规则。在c++ 11中,除了复制省略之外,通过移动语义,按值传递可以获得一些重大的胜利。

If you really want speed, Profile your code.

如果您真的想要速度,请对代码进行概要分析。

#3


3  

If you really don't mind exposing non-references in your API (which SHOULD BE a sign that internally you will be copying/assigning given object) then using copies is ok.

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

Copy elision is faster than moving, and if it can't be elided (for various reasons, like too long call chain of dependent function calls), then C++ guarantees move semantics.

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