谁支付不通过const引用的成本

时间:2016-08-03 10:13:19

标签: c++

假设:

void foo(std::vector<int> v);

void bar()
{
    std::vector<int> v = ...; // many items
    foo(v);
}

分析工具中的哪些内容会显示为热路径?它是std::vector<T>的复制构造函数,运行时还是操作系统?我记得在学校(我不是C ++开发人员,只是与一些人合作),这将复制v,这可能需要时间。我知道签名如:

void foo(const std::vector<int>& v);

避免这种可能代价高昂的复制操作。

4 个答案:

答案 0 :(得分:5)

按值复制std::vector<T>可能有三件事:

  1. 内存管理器(C ++运行时或自定义分配器)在其存储中搜索新向量的可用内存块。如果能找到它,那就进入第3步。
  2. 内存管理器(C ++运行时或自定义分配器)从操作系统请求更多内存。这个调用本身相对便宜(和系统调用一样多),因为操作系统以惰性模式提供内存 - 实际分配是在第一次写入请求的VM页面时发生的。
  3. 编译器生成的代码(应用程序)使用复制c-tor为新向量中的每个元素执行就地new,如果T不是简单的可复制构造的。否则,调用memcpy()或其优化的矢量化(在sse意义上)对应物。在第一次写入新内存之前,如果它是从#2中的OS获取的,则操作系统将需要实际分配新的VM页面并且可能触发RAM访问(硬件),TLB查找(硬件+ OS)交换进/出(OS)。
  4. 在您的具体示例中,T可以简单地进行复制构造,因此最坏情况的开销是C++运行时内存块查找+ sbrk()系统调用+ memcpy()调用。

答案 1 :(得分:2)

通过值与const引用进行参数的决定应该考虑函数计划对值进行的操作。如果函数仅计划从现有值读取,那么const引用是最有效的选择。如果被调用函数将已引用对象的副本作为其实现的一部分,则传递值有可能更快,因为调用者可以移动构造参数(如果有意义),并且不需要复制。

void foo(std::vector<int> v);

void bar()
{
    std::vector<int> v = ...; // many items
    foo(std::move(v)); // no copy needed here
}

答案 2 :(得分:1)

您可以期待调用document.body.style.opacity='0.3'; document.getElementById("div_block").style.opacity='0.3'; 的副本构造函数。

如果在执行操作中绝对没有副作用,则允许使用聪明的编译器来优化值副本。此外,如果函数 definition 被标记为std::vector<int>,那么如果编译器尊重您的请求(它不必),则不会获取值副本。

您最好的选择确实是通过inline参考传递v。这不仅可以避免值复制,而且还意味着被调用的函数不能修改传递的参数。

答案 3 :(得分:1)

编译器只需要发出代码as-if运行代码的代码,即可观察行为是相同的。

可能的热点应该是bar中的赋值运算符,因为您需要创建原始向量或复制构造函数。

在您的示例中,编译器可以以不同的方式重写它,如果它可以推断v从未在bar中再次使用,那么它可能会将调用foo(v);实现为{{ 1}}(就像已经提到过的@MSalters)。 这会将foo(std::move(v))留作v中的死对象,然后将其销毁。

或者,编译器可能只是在调用堆栈上创建bar()并保存自己对析构函数的调用,从而有效地使得按值调用。