当前位置: 首页 > news >正文

详解C++编译器优化技术

前言

注1:vc6、vs没有提供编译选项来关闭该优化,无论是debug还是release都会进行RVO和复制省略优化

注2:vc6、vs2005以下及vs2005+ Debug上不支持NRVO优化,vs2005+ Release支持NRVO优化

注3:g++支持这三种优化,并且可通过编译选项:-fno-elide-constructors来关闭优化

RVO

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

#include <stdio.h>

classA

{

public:

A()

{

printf("%p construct\n",this);

}

A(constA& cp)

{

printf("%p copy construct\n",this);

}

~A()

{

printf("%p destruct\n",this);

}

};

A GetA()

{

returnA();

}

intmain()

{

{

A a = GetA();

}

return0;

}

在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++代码模拟一下其优化行为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#include <new>

A& GetA(void* p)

{

//由于p的内存是从外部传入的,函数返回后仍然有效,因此返回值可为A&

//vs中,以下代码还可以写成:

// A& o = *((A*)p);

// o.A::A();

// return o;

return*new(p) A();// placement new

}

intmain()

{

{

charbuf[sizeof(A)];

A& a = GetA(buf);

a.~A();

}

return0;

}

NRVO

g++编译器、vs2005+ Release(开启/O2及以上优化开关)

修改上述代码,将GetA的实现修改成:

1

2

3

4

5

A GetA()

{

A o;

returno;

}

在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上的行为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

#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

}

intmain()

{

{

charbuf[sizeof(A)];

A& a = GetA(buf);

a.~A();

}

return0;

}

注:与g++、vs2005+ Release相比,vc6、vs2005以下、vs2005+ Debug只优化掉了返回值到变量a的拷贝,命名局部变量o没有被优化掉,所以最后一共有2次构造和析构的调用

复制省略

典型情况是:调用构造函数进行值类型传参

1

2

3

4

5

6

7

8

9

10

11

12

voidFunc(A a)

{

}

intmain()

{

{

Func(A());

}

return0;

}

在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++代码模拟一下其优化行为:

1

2

3

4

5

6

7

8

9

10

11

12

voidFunc(constA& a)

{

}

intmain()

{

{

Func(A());

}

return0;

}

优化失效的情况

开启g++优化,得到以下各种失效情况的输出结果:

(1)根据不同的条件分支,返回不同变量

1

2

3

4

5

6

7

8

9

10

11

12

13

14

A GetA(boolbflag)

{

A a1, a2;

if(bflag)

returna1;

returna2;

}

intmain()

{

A a = GetA(true);

return0;

}

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==赋值运算符)以上就是详解C++编译器优化技术的详细内容

http://www.cnnetsun.cn/news/2526536.html

相关文章:

  • 如何用Godot RE Tools实现完整的Godot项目逆向工程恢复?
  • 5分钟实现游戏手柄控制PC的终极指南:Gopher360让你的客厅电脑焕然一新
  • C/C++高精度算法的实现
  • 告别仿真报错!手把手教你用Quartus II 18.1和ModelSim 10.5c创建第一个Testbench
  • 五分钟完成Node.js服务对接Taotoken多模型API的配置教程
  • Unity图表性能优化:从折线图到饼图的底层实现与避坑指南
  • 如何3分钟掌握AI智能填充:Fillinger终极实战指南
  • 大模型部署困境破局:Qwen模型ONNX格式转换与多平台部署实战
  • 新一代高性能SAR舰船智能检测数据集SSDD:从集中到分散的渐进式检测范式革新
  • 企业内训系统集成Taotoken实现多模型AI助教与可控的交互成本
  • 新手开发者首次接触 Taotoken 控制台的功能导览与核心操作
  • MATLAB机器人工具箱:从零到精通的机器人开发全攻略
  • Arduino UNO R3引脚图详解与供电方案选择:从USB到外接电源的避坑指南
  • Winhance中文版终极指南:3步让你的Windows飞起来
  • 注意力机制的幕后:它到底转化了什么?输入、输出与词向量的类比
  • 《纳瓦尔宝典》幸福篇精读:程序员如何在敲码之余获得内心的平静与幸福
  • 渐变不自然?曝光过曝?色阶断裂?Midjourney渐变风格全流程调优手册,30分钟重塑视觉一致性
  • SELinux报错排查指南:从AVC拒绝日志到精准修复
  • SDL2初始化函数全解析:从SDL_Init到SDL_Quit,你的游戏引擎第一行代码该怎么写?
  • 在无MMU的RISC-V MCU上移植Linux 6.10内核:基于HPM6360的实践指南
  • 如何高效配置CharacterAI Python API:完整使用指南与最佳实践
  • 鸿蒙 PC:从“用户点击”到“AI 调度”
  • Python自动化CAD处理终极指南:用ezdxf库实现DXF文件高效操作
  • 2026 最新claude-code 实用技巧指南 看这一篇就够了
  • 3步实现Adobe全家桶完整激活:终极破解方案详解
  • 如何永久保存你的微信聊天记录:WeChatMsg完整解决方案指南
  • vSphere 7.0环境搭建:除了安装vCSA,这些后期配置(许可证、告警、备份)你做了吗?
  • ULINK调试器独立编程HEX文件全指南
  • 高云Arora-V 60K FPGA图像开发板:从硬件架构到实时视觉系统实战
  • 3个技巧彻底掌握泰坦之旅装备管理神器