假设:
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);
避免这种可能代价高昂的复制操作。
答案 0 :(得分:5)
按值复制std::vector<T>
可能有三件事:
new
,如果T
不是简单的可复制构造的。否则,调用memcpy()
或其优化的矢量化(在sse
意义上)对应物。在第一次写入新内存之前,如果它是从#2中的OS获取的,则操作系统将需要实际分配新的VM页面并且可能触发RAM访问(硬件),TLB查找(硬件+ OS)交换进/出(OS)。在您的具体示例中,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()
并保存自己对析构函数的调用,从而有效地使得按值调用。