C++编译器优化技术:RVO、NRVO和复制省略
现代编译器缺省会使用RVO(return value optimization,返回值优化)、NRVO(named return value optimization、命名返回值优化)和复制省略(Copy elision)技术,来减少拷贝次数来提升代码的运行效率
注1:vc6、vs没有提供编译选项来关闭该优化,无论是debug还是release都会进行RVO和复制省略优化
注2:vc6、vs2005以下及vs2005+ Debug上不支持NRVO优化,vs2005+ Release支持NRVO优化
注3:g++支持这三种优化,并且可通过编译选项:-fno-elide-constructors来关闭优化
RVO
#include <stdio.h>
class A
{
public:
A()
{
printf("%p construct\n", this);
}
A(const A& cp)
{
printf("%p copy construct\n", this);
}
~A()
{
printf("%p destruct\n", this);
}
}; A GetA()
{
return A();
} int main()
{
{
A a = GetA();
} return ;
}
在g++和vc6、vs中,上述代码仅仅只会调用一次构造函数和析构函数 ,输出结果如下:
0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct
在g++中,加上-fno-elide-constructors选项关闭优化后,输出结果如下:
0x7ffc46947d4f construct // 在函数GetA中,调用无参构造函数A()构造出一个临时变量temp
0x7ffc46947d7f copy construct // 函数GetA return语句处,把临时变量temp做为参数传入并调用拷贝构造函数A(const A& cp)将返回值ret构造出来
0x7ffc46947d4f destruct // 函数GetA执行完return语句后,临时变量temp生命周期结束,调用其析构函数~A()
0x7ffc46947d7e copy construct // 函数GetA调用结束,返回上层main函数后,把返回值变量ret做为参数传入并调用拷贝构造函数A(const A& cp)将变量A a构造出来
0x7ffc46947d7f destruct // A a = GetA()语句结束后,返回值ret生命周期结束,调用其析构函数~A()
0x7ffc46947d7e destruct // A a要离开作用域,生命周期结束,调用其析构函数~A()
注:临时变量temp、返回值ret均为匿名变量
下面用c++代码模拟一下其优化行为:
#include <new>
A& GetA(void* p)
{
//由于p的内存是从外部传入的,函数返回后仍然有效,因此返回值可为A&
//vs中,以下代码还可以写成:
// A& o = *((A*)p);
// o.A::A();
// return o;
return *new (p) A(); // placement new
} int main()
{
{
char buf[sizeof(A)];
A& a = GetA(buf);
a.~A();
} return ;
}
NRVO
g++编译器、vs2005+ Release(开启/O2及以上优化开关)
修改上述代码,将GetA的实现修改成:
A GetA()
{
A o;
return o;
}
在g++、vs2005+ Release中,上述代码也仅仅只会调用一次构造函数和析构函数 ,输出结果如下:
0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct
g++加上-fno-elide-constructors选项关闭优化后,和上述结果一样
0x7ffc46947d4f construct
0x7ffc46947d7f copy construct
0x7ffc46947d4f destruct
0x7ffc46947d7e copy construct
0x7ffc46947d7f destruct
0x7ffc46947d7e destruct
但在vc6、vs2005以下、vs2005+ Debug中,没有进行NRVO优化,输出结果为:
18fec4 construct // 在函数GetA中,调用无参构造函数A()构造出一个临时变量o
18ff44 copy construct // 函数GetA return语句处,把临时变量o做为参数传入并调用拷贝构造函数A(const A& cp)将返回值ret构造出来
18fec4 destruct // 函数GetA执行完return语句后,临时变量o生命周期结束,调用其析构函数~A()
18ff44 destruct // A a要离开作用域,生命周期结束,调用其析构函数~A()
下面用c++代码模拟一下vc6、vs2005以下、vs2005+ Debug上的行为:
#include <new>
A& GetA(void* p)
{
A o;
//由于p的内存是从外部传入的,函数返回后仍然有效,因此返回值可为A&
//vs中,以下代码还可以写成:
// A& t = *((A*)p);
// t.A::A(o);
// return t;
return *new (p) A(o); // placement new
} int main()
{
{
char buf[sizeof(A)];
A& a = GetA(buf);
a.~A();
} return ;
}
注:与g++、vs2005+ Release相比,vc6、vs2005以下、vs2005+ Debug只优化掉了返回值到变量a的拷贝,命名局部变量o没有被优化掉,所以最后一共有2次构造和析构的调用
复制省略
典型情况是:调用构造函数进行值类型传参
void Func(A a)
{
} int main()
{
{
Func(A());
} return ;
}
在g++和vc6、vs中,上述代码仅仅只会调用一次构造函数和析构函数 ,输出结果如下:
0x7ffeb5148d0f construct
0x7ffeb5148d0f destruct
在g++中,加上-fno-elide-constructors选项关闭优化后,输出结果如下:
0x7ffc53c141ef construct // 在main函数中,调用无参构造函数构造实参变量o
0x7ffc53c141ee copy construct // 调用Func函数后,将实参变量o做为参数传入并调用拷贝构造函数A(const A& cp)将形参变量a构造出来
0x7ffc53c141ee destruct // 函数Func执行完后,形参变量a生命周期结束,调用其析构函数~A()
0x7ffc53c141ef destruct // 返回main函数后,实参变量o要离开作用域,生命周期结束,调用其析构函数~A()
下面用c++代码模拟一下其优化行为:
void Func(const A& a)
{
} int main()
{
{
Func(A());
} return ;
}
优化失效的情况
开启g++优化,得到以下各种失效情况的输出结果:
(1)根据不同的条件分支,返回不同变量
A GetA(bool bflag)
{
A a1, a2;
if (bflag)
return a1;
return a2;
} int main()
{
A a = GetA(true); return ;
}
0x7ffc3cca324f construct
0x7ffc3cca324e construct
0x7ffc3cca327f copy construct
0x7ffc3cca324e destruct
0x7ffc3cca324f destruct
0x7ffc3cca327f destruct
注1:2次缺省构造函数调用:用于构造a1、a2
注2:1次拷贝构造函数调用:用于拷贝构造返回值
注3:这儿仍然用右值引用优化掉了一次拷贝函数调用:返回值赋值给a
(2)返回参数变量
(3)返回全局变量
(4)返回复合数据类型中的成员变量
(5)返回值赋值给已构造好的变量(此时会调用operator==赋值运算符)
参考
What are copy elision and return value optimization?
Named Return Value Optimization in Visual C++ 2005
C++编译器优化技术:RVO、NRVO和复制省略的更多相关文章
- 返回值优化(RVO)
C++的函数中,如果返回值是一个对象,那么理论上它不可避免的会调用对象的构造函数和析构函数,从而导致一定的效率损耗.如下函数所示: A test() { A a; return a; } 在test函 ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part6:Move语义和编译器优化
本文为第六部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- 【M20】协助完成“返回值优化(RVO)”
1.方法返回对象,会导致临时对象的产生,这降低了效率,const Rational operator* (const Rational& lhs,Rational& rhs).有没有什 ...
- java编译期优化与执行期优化技术浅析
java语言的"编译期"是一段不确定的过程.由于它可能指的是前端编译器把java文件转变成class字节码文件的过程,也可能指的是虚拟机后端执行期间编译器(JIT)把字节码转变成机 ...
- 【深入理解JAVA虚拟机】第4部分.程序编译与代码优化.2.运行期优化。这章提到的具体的优化技术,应该对以后做性能工作会有帮助。
1.概述 Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”(Hot Spot Code). 为了提高 ...
- 《深入理解java虚拟机》学习笔记之编译优化技术
郑重声明:本片博客是学习<深入理解Java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序员有一个共识,以编译方式执行本地代码比解释方式更快,之所以有这样的共识,除去虚拟机解释 ...
- JDK and JRE File Structure JAVA_HOME HotSpot优化技术
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jdkfiles.html Java Platform, Standard ...
- java编译器优化和运行期优化
概述 最近在看jvm优化,总结一下学习的相关知识 (一)javac编译器 编译过程 1.解析与填充符号表过程 1).词法.语法分析 词法分析将源代码的字符流转变为标记集合,单个字符是程序编 ...
- node.js背后的引擎V8及优化技术
本文将挖掘V8引擎在其它方面的代码优化,如何写出高性能的代码,及V8的性能诊断工具.V8是chrome背后的javascript引擎,因此本文的相关优化经验也适用于基于chrome浏览器的javasc ...
随机推荐
- 联合查询和数据库设计e-r图
联合查询: 联合查询的关键字是: union 基本含义 联合查询就是将两个select语句的查询结果“层叠”到一起成为一个“大结果”. 两个查询结果的能够进行“联合”的先觉条件是:结果字段数相等. 就 ...
- Redis sorted set 常用命令介绍
Redis sorted set 使用: Redis 有序集合(sorted set) Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员. 不同的是每个元素都会关联一个 ...
- Android(常用)主流UI开源库整理
这几天刚做完一个项目..有点空余时间,就想着吧这一两年做的项目中的UI界面用到的一些库整理一下.后来想了一下,既然要整理,就把网上常用的 AndroidUI界面的主流开源库 一起整理一下,方便查看. ...
- 可编程逻辑(FPGA)与硬核处理器(HPS)之间互联的结构
本周我想进一步探究可编程逻辑(FPGA)与硬核处理器(HPS)之间互联的结构.我发现了三种主要方式,它们是如何映射并处理通信的,哪些组件需要管控时序并且有访问权限. AXI Bridge 为了能够实现 ...
- mysql重点中的重点---->查询中的关键字优先级
1.from 找到表 2.where 拿着where指定的约束条件,去文件/表中取出一条条记录 3.group by 将取出的一条条记录进行分组group by ,如果没有group by ,则整体作 ...
- 记Linux下一次乱码事件
近来需要对着教程敲代码,但是之前在Windows上的压缩包在Linux解压后发生了乱码,主要是文件内乱码,文件名还是正常的.搜索“Linux rar解压乱码“试了一圈也没解决.不过到是发现了winra ...
- C# WPF有趣的登录加载窗体
时间如流水,只能流去不流回! 点赞再看,养成习惯,这是您给我创作的动力! 本文 Dotnet9 https://dotnet9.com 已收录,站长乐于分享dotnet相关技术,比如Winform.W ...
- 【nginx+keepalived】nginx+keepalived搭建高可用
一.结构及环境 1.1 环境介绍 操作系统:centos7 nginx+keepalived:106.53.73.200 master nginx+keepalived:182.254.184.102 ...
- Redis两种持久化策略分析
Redis专题地址:https://www.cnblogs.com/hello-shf/category/1615909.html SpringBoot读源码系列:https://www.cnblog ...
- Android 菜单 Menu
@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to th ...