编译器的过度优化

这篇具有很好参考价值的文章主要介绍了编译器的过度优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

单例模式

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

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());

上述解决方法之所以能行,是因为编译器对于「跨越语句的各项操作」没有重新排列的自由,只有在一条语句内它才拥有那个自由。文章来源地址https://www.toymoban.com/news/detail-420541.html

到了这里,关于编译器的过度优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AI编译器-图常见优化算法-算子融合

    算子融合(Operator Fusion)是深度学习编译器中的一种优化技术,它可以将多个算子合并为一个更大的算子,以减少计算和内存访问的开销。以下是一些常见的算子融合例子: 卷积和池化融合:将卷积层和池化层融合为一个算子,减少内存访问和计算的开销。 多个全连接层融

    2024年02月10日
    浏览(49)
  • 性能优化:编译器优化选项 -O2/-O3 究竟有多强大?

    之前的“性能优化的一般策略及方法”一文中介绍了多种性能优化的方法。根据以往的项目经验, 开启编译器优化选项 可能是立竿见影、成本最低、效果最好的方式了。 这么说可能还不够直观,举个真实的例子:我所参与的自动驾驶的项目中,无需修改任何代码,仅仅增加

    2024年03月15日
    浏览(48)
  • C++代码性能优化的好处与缺点?有哪些编译器优化选项?

    性能优化是C++编程中的一个重要方面,它可以带来许多好处,但也有一些潜在的缺点。 以下是C++代码性能优化的一些优缺点: 优点: 提高执行速度 : 优化后的代码可以更快地执行,这对于需要处理大量数据或需要快速响应的应用程序尤其重要。 减少资源消耗 : 优化可以减少

    2024年03月27日
    浏览(54)
  • C++ | 探究拷贝对象时的一些编译器优化

    👑作者主页:@烽起黎明 🏠学习社区:烈火神盾 🔗专栏链接:C++ 在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的 经过深度探索类的六大天选之子学习,我们讲到了拷贝构造一些基本概念和调用形式 经过构造函数

    2023年04月19日
    浏览(85)
  • 形象谈JVM-第三章-即时编译器优化技术

    即时编译器优化技术一览: 相信许多同学看完这个表格,脑子里面嗡嗡的,这些名字也是晦涩难懂,要实现这些优化的技术确实有比较大的难度,但是咱们只是学习,去理解这些技术,其实并不难,下面咱们直接开讲。 首先需要明确一点的,作者是为了讲解方便,使用java的

    2024年02月12日
    浏览(45)
  • Keil5,ARM编译器 软件优化注意事项

    循环是大多数程序中的常见结构。由于大量的执行时间通常花费在循环中,因此值得关注时间关键循环。 如果不谨慎地编写,环路终止条件可能会导致大量开销。在可能的情况下: 使用简单的终止条件。 写入倒计时到零循环。 使用  unsigned int  类型的计数器。 测试与零的

    2024年02月03日
    浏览(52)
  • 即时编译器对于字段读取及存储指令的优化是什么?

    本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见100个问题搞定Java虚拟机 即时编译器会优化实例字段以及静态字段访问,以减少总的内存访问数目。 具体来说,

    2023年04月08日
    浏览(73)
  • 【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化

    目录 1. 再谈构造 1.1 成员变量的初始化(初始化列表) 1.2 初始化列表的行为 1.3 explicit  2. 类中的static成员 2.1 静态成员变量 2.2 静态成员函数 3. 友元 3.1 友元函数 3.1 友元类 4. 内部类  5. 匿名对象  6. 对象拷贝时候的编译器优化           为什么还要去看初始化的问

    2024年02月13日
    浏览(41)
  • 计算机体系结构基础知识介绍之缓存性能的十大进阶优化之编译器优化和硬件预取(六)

    处理器和主内存之间不断扩大的性能差距促使编译器编写者仔细检查内存层次结构,看看编译时优化是否可以提高性能。再次,研究分为指令缺失的改进和数据缺失的改进。接下来介绍的优化可以在许多现代编译器中找到。 有些程序具有嵌套循环,以非连续的顺序访问内存中

    2024年02月12日
    浏览(69)
  • proteus结合keil-arm编译器构建STM32单片机项目进行仿真

        proteus是可以直接创建设计图和源码的,但是源码编译它需要借助keil-arm编译器,也就是我们安装keil-mdk之后自带的编译器。     下面给出一个完整的示例,主要是做一个LED灯闪烁的效果。     新建工程指定路径,Schematic,PCB layout都选择默认,在最后创建项目工程向导的时

    2024年02月13日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包