Mar*_*sen 26 c++ compiler-optimization c++11
返回值优化(RVO)是一种涉及复制省略的优化技术,它消除了在某些情况下为保存函数返回值而创建的临时对象.我一般都了解RVO的好处,但我有几个问题.
该标准在本工作草案的第12段第32段(强调我的)中说明了以下内容.
当满足某些条件时,允许实现省略类对象的复制/移动构造,即使该对象的复制/移动构造函数和/或析构函数具有副作用.在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间.没有优化就被破坏了.
然后,当实现可以执行此优化时,它会列出许多条件.
关于这种潜在的优化,我有几个问题:
我习惯于约束优化,以至于它们无法改变可观察的行为.此限制似乎不适用于RVO.我是否需要担心标准中提到的副作用?是否存在可能导致问题的角落案例?
作为程序员,我需要做什么(或不做)才能执行此优化?例如,以下是否禁止使用复制省略(由于move):
std::vector<double> foo(int bar){
std::vector<double> quux(bar,0);
return std::move(quux);
}
Run Code Online (Sandbox Code Playgroud)
我将此作为一个新问题发布,因为我提到的具体问题在其他相关问题中没有直接回答.
Cas*_*eri 14
我习惯于约束优化,以至于它们无法改变可观察的行为.
这是对的.作为一般规则 - 称为as -if规则 - 如果更改不可观察,编译器可以更改代码.
此限制似乎不适用于RVO.
是.OP中引用的子句给出了as-if规则的一个例外,允许复制构造被省略,即使它有副作用.请注意,RVO只是一个copy-elision(C++ 11 12.8/31中的第一个要点).
我是否需要担心标准中提到的副作用?
如果复制构造函数具有副作用,使得执行时复制省略会导致问题,那么您应该重新考虑设计.如果这不是您的代码,您应该考虑更好的替代方案.
作为程序员,我需要做什么(或不做)才能执行此优化?
基本上,如果可能,返回具有与函数返回类型相同的cv非限定类型的局部变量(或临时).这允许RVO但不强制执行它(编译器可能不执行RVO).
例如,以下是否禁止使用复制省略(由于移动):
// notice that I fixed the OP's example by adding <double>
std::vector<double> foo(int bar){
std::vector<double> quux(bar, 0);
return std::move(quux);
}
Run Code Online (Sandbox Code Playgroud)
是的,它确实是因为你没有返回局部变量的名称.这个
std::vector<double> foo(int bar){
std::vector<double> quux(bar,0);
return quux;
}
Run Code Online (Sandbox Code Playgroud)
允许RVO.有人可能会担心如果不执行RVO,那么移动比应对更好(这可以解释std::move上面的使用).别担心.所有主要编译器都将在这里执行RVO(至少在发布版本中).即使编译器不执行RVO但满足RVO的条件,它也会尝试执行移动而不是复制.总之,使用std::move上面肯定会采取行动.不使用它可能既不复制也不移动任何东西,在最坏的(不太可能的)情况下,将会移动.
(更新:正如haohaolee指出的那样(见评论),以下段落不正确.但是,我把它们留在这里是因为它们提出了一个可能适用于没有构造函数的类的想法std::initializer_list(参见参考资料)为了std::vector,haohaolee找到了一个解决方法.)
在这个例子中,您可以通过返回一个braced-init-list来强制RVO(严格来说,这不再是RVO,而是让我们继续这样调用),从中可以创建返回类型:
std::vector<double> foo(int bar){
return {bar, 0}; // <-- This doesn't work. Next line shows a workaround:
// return {bar, 0.0, std::vector<double>::allocator_type{}};
}
Run Code Online (Sandbox Code Playgroud)
看到这篇文章和R. Martinho Fernandes的精彩回答.
小心点!返回类型是std::vector<int>上面的最后一个代码与原始代码有不同的行为.(这是另一个故事.)
我强烈建议阅读Stanely B. Lippman的"Inside the C++ Object Model",以获取有关命名返回值优化如何工作的详细信息和一些历史背景.
例如,在第2.1章中,他有关于命名返回值优化的说法:
在诸如bar()之类的函数中,所有返回语句都返回相同的命名值,编译器本身可以通过将result参数替换为命名返回值来优化函数.例如,给定bar()的原始定义:
Run Code Online (Sandbox Code Playgroud)X bar() { X xx; // ... process xx return xx; }__result被编译器替换为xx:
Run Code Online (Sandbox Code Playgroud)void bar( X &__result ) { // default constructor invocation // Pseudo C++ Code __result.X::X(); // ... process in __result directly return; }(....)
尽管NRV优化提供了显着的性能改进,但是对这种方法存在一些批评.一个是因为优化是由编译器无声地完成的,无论它是否实际执行并不总是清楚的(特别是因为很少有编译器记录它的实现程度或是否完全实现).第二,随着功能变得更加复杂,优化变得更加难以应用.例如,在cfront中,仅当所有命名的return语句都出现在函数的顶层时才应用优化.引入带有return语句的嵌套本地块,然后cfront静静地关闭优化.
| 归档时间: |
|
| 查看次数: |
1738 次 |
| 最近记录: |