rvo文档_为什么Visual Studio在此情况下不执行返回值优化(RVO)

本文讨论了Visual Studio 2013在特定情况下未执行返回值优化(RVO)的问题。作者通过示例代码展示了RVO预期发生但未执行的情况,并探讨了可能的原因,包括编译器对C++11移动构造函数的支持。尽管有工作-around,如定义移动构造函数或使用NVRO,作者希望理解RVO失败的原因以便于未来预测。文章建议当优化未按预期工作时,可以向微软报告问题或检查汇编输出以确认优化是否发生。
摘要由CSDN通过智能技术生成

I was answering a question and recommending return by-value for a large type because I was confident the compiler would perform return-value optimization (RVO). But then it was pointed out to me that Visual Studio 2013 was not performing RVO on my code.

I've found a question here regarding Visual Studio failing to perform RVO but in that case the conclusion seemed to be that if it really matters Visual Studio will perform RVO. In my case it does matter, it makes a significant impact to performance which I've confirmed with profiling results. Here is the simplified code:

#include

#include

#include

struct Foo {

std::vector v;

Foo(std::vector _v) : v(std::move(_v)) {}

};

Foo getBigFoo() {

std::vector 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";

}

}

I'm expecting the compiler to perform RVO on the return type from getBigFoo(). But it appears to be copying Foo instead.

I'm aware that the compiler will create a copy-constructor for Foo. I'm also aware that unlike a compliant C++11 compiler Visual Studio does not create a move-constructor for Foo. But that should be OK, RVO is a C++98 concept and works without move-semantics.

So, the question is, is there a good reason why Visual Studio 2013 does not perform return value optimization in this case?

I know of a few workarounds. I can define a move-constructor for Foo:

Foo(Foo&& in) : v(std::move(in.v)) {}

which is fine, but there are a lot of legacy types out there that don't have move-constructors and it would be nice to know I can rely on RVO with those types. Also, some types may be inherently copyable but not movable.

If I change from RVO to NVRO (named return value optimization) then Visual Studio does appear to perform the optimization:

Foo foo(std::move(v))

return foo;

which is curious because I thought NVRO was less reliable than RVO.

Even more curious is if I change the constructor of Foo so it creates and fills the vector:

Foo(size_t num) : v(num) {

std::iota(v.begin(), v.end(), 0); // Fill vector with non-trivial data

}

instead of moving it in then when I try to do RVO, it works:

Foo getBigFoo() {

return Foo(1000000);

}

I'm happy to go with one of these workarounds but I'd like to be able to predict when RVO might fail like this in the future, thanks.

Edit2: Why don't I just write return v;?

For a start, it doesn't help. Profiler results show that Visual Studio 2013 still copies the vector if I just write return v; And even if it did work it would only be a workaround. I'm not trying to actually fix this particular piece of code, I'm trying to understand why RVO fails so I can predict when it might fail in the future. It is true that it is a more concise way of writing this particular example but there are plenty of cases where I couldn't just write return v;, for example if Foo had additional constructor parameters.

解决方案

If the code looks like it should be optimized, but is not getting optimized I would submit bug here http://connect.microsoft.com/VisualStudio or raise a support case with Microsoft.

This article, although it is for VC++2005 (I couldn't find a current version of document) does explain some scenarios where it won't work. http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx#nrvo_cpp05_topic3

If we want to be sure the optimization has occurred, one possibility is to check the assembly output. This could be automated as a build task if desired.

This requires generating .asm output using /FAs option like so:

cl test.cpp /FAs

Will generate test.asm.

A potential example in PowerShell below, which can be used in this way:

PS C:\test> .\Get-RVO.ps1 C:\test\test.asm test.cpp

NOT RVO test.cpp - ; 13 : return Foo(std::move(v));// Expecting RVO to happen here.

PS C:\test> .\Get-RVO.ps1 C:\test\test_v2.optimized.asm test.cpp

RVO OK test.cpp - ; 13 : return {std::move(v)}; // Expecting RVO to happen here.

PS C:\test>

The script:

# Usage Get-RVO.ps1

# Example .\Get-RVO.ps1 C:\test\test.asm test.cpp

[CmdletBinding()]

Param(

[Parameter(Mandatory=$True,Position=1)]

[string]$assemblyFilename,

[Parameter(Mandatory=$True,Position=2)]

[string]$cppFilename

)

$sr=New-Object System.IO.StreamReader($assemblyFilename)

$IsInReturnSection=$false

$optimized=$true

$startLine=""

$inFile=$false

while (!$sr.EndOfStream)

{

$line=$sr.ReadLine();

# ignore any files that aren't our specified CPP file

if ($line.StartsWith("; File"))

{

if ($line.EndsWith($cppFilename))

{

$inFile=$true

}

else

{

$inFile=$false

}

}

# check if we are in code section for our CPP file...

if ($inFile)

{

if ($line.StartsWith(";"))

{

# mark start of "return" code

# assume optimized, unti proven otherwise

if ($line.Contains("return"))

{

$startLine=$line

$IsInReturnSection=$true

$optimized=$true

}

}

if ($IsInReturnSection)

{

# call in return section, not RVO

if ($line.Contains("call"))

{

$optimized=$false

}

# check if we reached end of return code section

if ($line.StartsWith("$") -or $line.StartsWith("?"))

{

$IsInReturnSection=$false

if ($optimized)

{

"RVO OK $cppfileName - $startLine"

}

else

{

"NOT RVO $cppfileName - $startLine"

}

}

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值