我回答了a question并建议使用return by-value for a large type,因为我确信编译器会执行return-value optimization (RVO)。但后来有人向我指出,Visual Studio 2013未在我的代码上执行RVO。为什么Visual Studio在这种情况下不执行返回值优化(RVO)
我发现a question here关于Visual Studio未能执行RVO,但在这种情况下,结论似乎是,如果它真的很重要Visual Studio将执行RVO。在我的情况下,它确实是的问题,它对我已经用分析结果证实的性能产生了重大影响。下面是简化的代码:
#include <vector>
#include <numeric>
#include <iostream>
struct Foo {
std::vector<double> v;
Foo(std::vector<double> _v) : v(std::move(_v)) {}
};
Foo getBigFoo() {
std::vector<double> v(1000000);
std::iota(v.begin(), v.end(), 0); // Fill vector with non-trivial data
return Foo(std::move(v)); // Expecting RVO to happen here.
}
int main() {
std::cout << "Press any key to start test...";
std::cin.ignore();
for (int i = 0; i != 100; ++i) { // Repeat test to get meaningful profiler results
auto foo = getBigFoo();
std::cout << std::accumulate(foo.v.begin(), foo.v.end(), 0.0) << "\n";
}
}
我期待从getBigFoo()
返回类型进行RVO的编译器。但它似乎是复制Foo
。
我知道编译器will create a copy-constructor为Foo
。我也知道,与符合C++ 11编译器Visual Studio does not create a move-constructor的Foo
不同。但是,这应该是确定的,RVO是一个C++ 98的概念,没有移动语义。
因此,问题是,Visual Studio 2013在这种情况下不执行返回值优化的原因是否有充分的理由?
我知道一些解决方法。我可以定义Foo
此举构造函数:
Foo(Foo&& in) : v(std::move(in.v)) {}
这是很好的,但也有很多传统类型的,在那里,没有动,构造函数,这将是很高兴知道我可以依靠与这些类型的RVO。另外,某些类型可能固有地可复制但不可移动。
如果我从RVO改变NVRO(命名返回值优化),那么Visual Studio中确实出现执行优化:
Foo foo(std::move(v))
return foo;
,因为我认为NVRO比RVO可靠少这是好奇。
更好奇的是,如果我改变Foo
因此它创建的构造和填充vector
:
Foo(size_t num) : v(num) {
std::iota(v.begin(), v.end(), 0); // Fill vector with non-trivial data
}
代替然后移动它时,我尝试做RVO,它的工作原理:
Foo getBigFoo() {
return Foo(1000000);
}
我很高兴能够采用这些解决方法之一,但我希望能够预测RVO何时可能会在未来发生此类故障,谢谢。
编辑:More concise live demo从@dyp
EDIT2:我为什么不只是写return v;
?
首先,它没有帮助。事件探查器结果显示,如果我只写return v;
,Visual Studio 2013仍会复制向量,即使它工作,它也只是一种解决方法。我并没有试图修复这段代码,我想了解RVO为什么会失败,所以我可以预测它何时可能会失败。确实,这是编写这个特定示例的更简洁的方式,但是有很多情况下我不能仅仅编写return v;
,例如,如果Foo
具有其他构造函数参数。
在黑暗中总刺:你观察到不同的行为,如果你使用'返回Foo(std :: move(v));'(圆括号)而不是'return Foo {std :: move(v)};'(花括号)?我不认为这会有所作为,但我不愿意在这方面下注。 – 2014-09-21 20:53:26
@Insilico,不,同样的行为,我只在那里使用花括号(统一初始化),因为它有点过于接近最令人烦恼的解析。我会改变它,除非抛出任何人... – 2014-09-21 20:57:41
当然,你可以使用'return {std :: move(v)};'因为这个构造函数不是显式的。这不需要任何(N)RVO,它被指定不创建临时。 – dyp 2014-09-21 21:00:34