编译器的过度优化

news/2024/11/26 5:28:56/

前言

编译器在进行优化的时候,可能为了效率而交换不相关的两条相邻指令的执行顺序。也就是指令重排,这也就引发了一些问题,下面就带大家看两个经典的问题。

单例模式

第一个例子来自单例模式的双加锁,下面是典型的双加锁的单例模式代码:

T* ptr = nullptr;T* GetInstance() {if (nullptr == ptr) {lock();if (nullptr == ptr) {ptr = new T;}unlock();}return ptr;
}

上面的代码看起来没问题,并且采用了双加锁,能减少锁的竞争。

我们知道 C++ 的 new 做了两件事:

  1. 调用 ::operator new 分配内存
  2. 调用构造函数

所以 ptr = new T 包含了三个步骤:

  1. 调用 ::operator new 分配内存
  2. 在内存的位置上调用构造函数
  3. 将内存的地址赋值给 ptr

在这三步中,2 和 3 的顺序是可以交换的。也就是说,有可能:有一个线程分配了内存并将地址赋值给 ptr 了,但还没有初始化该内存。另一线程检测 ptr 不为空,就直接拿去使用了,这时可能引起不可预料的结果。

通常情况下,可以调用 CPU 提供的一条指令来解决该情况,这指令被称为 barrier。一条 barrier 指令会阻止 CPU 将该指令交换到 barrier 之后,也不能将之后的指令交换到 barrier 之前。

#define barrier() __asm__ volaticle("lwsync") 
T* ptr = nullptr;T* GetInstance() {if (nullptr == ptr) {lock();if (nullptr == ptr) {T* tmp = new T;barrier();ptr = tmp;}unlock();}return ptr;
}

智能指针

有时我们会采用智能指针来管理内存,防止我们忘记释放或在某些场景手动释放十分麻烦。

我们有如下代码:

processQgw(shared_ptr<Qgw>(new Qgw), test());

令人遗憾的是,上述代码仍可能产生内存泄漏,并难以察觉。

编译器在调用函数前,需要准备好参数,所以上面代码会做以下三件事:

  • 调用 test
  • 执行 new Qgw
  • 调用 shared_ptr 构造函数

C++ 编译器会以什么次序完成这些事情呢?可以确定的是 new Qgw 一定在 调用 shared_ptr 之前完成,上述三件事也一定在调用 processQgw 之前完成。编译器可能以下列次序调用:

  1. 执行 new Qgw
  2. 调用 test
  3. 调用 shared_ptr 构造函数

如果 test 的调用导致异常,new Qgw 返回的指针就再也找不到了,也就引起了内存泄漏。因为我们还没将返回的指针置入智能指针,智能指针也就对这种情况无能为力了。

为避免这类问题:使用分离语句,分别写出创建 Qgw,将它置入一个智能指针内,然后再把智能指针传给 processQgw:

shared_ptr<Qgw> pq = new Qgw;
processQgw(pq, test());

上述解决方法之所以能行,是因为编译器对于「跨越语句的各项操作」没有重新排列的自由,只有在一条语句内它才拥有那个自由。


http://www.ppmy.cn/news/49741.html

相关文章

使用FFMPEG库将PCM编码为AAC

准备 ffmpeg 版本4.4 准备一段48000Hz 2 channel f32le 格式的PCM原始数据 这里我们直接使用ffmpeg命令行提取 ffmpeg -i beautlWorld.mp4 -ar 48000 -ac 2 -f f32le 48000_2_f32le.pcm -ar 采样率 -ac 音频通道 -f f32le 音频样本数据存储格式&#xff08;f32 ---- float…

如何高效提高倾斜摄影三维模型顶层合并的技术方法分析

如何高效提高倾斜摄影三维模型顶层合并的技术方法分析 1、倾斜摄影三维模型顶层合并 1.1倾斜摄影三维模型是一种基于倾斜摄影技术&#xff0c;通过多个角度拍摄同一区域的影像&#xff0c;利用计算机图像处理和三维重建技术生成的三维地理信息数据。由于一个大区域可能需要多块…

网络安全与攻防-同源策略

目录 同源策略&#xff08;浏览器控制&#xff09; 定义 思考&#xff1a; 跨域的N种方法 Jsonp 跨域资源共用&#xff08;CORS&#xff09; 预检&#xff08;OPTIONS请求&#xff09; 代理服务&#xff08;优先考虑&#xff09; 实战CORS&#xff08;Fetchnode.js&…

【Qt】QString与QChar的源码学习及二者与Unicode的关系【2023.04.20】

简介 本文是关于QString乱码的一些补充。主要就两点&#xff0c;QChar、QString底层存储的字符都是16进制的Unicode编码。会结合源码进行“刨根问祖”。 QChar QChar对应16位的Unicode字符集。 The QChar class provides a 16-bit Unicode character. In Qt, Unicode charact…

Redis问题

一、认识Redis 1. 什么是 Redis&#xff1f; Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。Redis 提供了多种数据类型来支持不同的业务场景&#…

Springboot结合线程池的使用

1.使用配置文件配置线程的参数 配置文件 thread-pool:core-size: 100max-size: 100keep-alive-seconds: 60queue-capacity: 1配置类 Component ConfigurationProperties("thread-pool") Data public class ThreadPoolConfig {private int coreSize;private int ma…

zabbix配置钉钉机器人告警

1.在钉钉上创建一个钉钉群组 2.在群组中添加一个机器人 3.配置zabbix server调用钉钉接口的代码(使用python) 查看是否有python环境 python --version 找到zabbix 的AlertScriptsPath目录路径 cat /etc/zabbix/zabbix_server.conf|grep AlertScriptsPath 将调用钉钉接口的py…

Python解析CANoe录制的blf文件asc文件通用方法

Python解析CANoe录制的blf文件&asc文件通用方法 一、背景 由于很多时候我们在录制日志文件的时候更愿意选择BLF文件,至少目前我见到的很多公司都是使用的BLF文件来作为最后的日志文件,相比较ASC文件,BLF文件属于二进制文件,所以没有办法通过文本编辑器直接读写,所以本…