C++语法|volatile关键字!从CPU角度进行理解

news2024/11/25 2:33:40

个人认为C++有着复杂、臃肿的语法系统,但是也正是因为这些特性,让我们在使用C++时既能深入到操作系统级的控制,也能抽象出来完全专注于一些业务问题。
这里为大家推荐一本书和汇编代码阅读网站!
《CPU眼里的C/C++》
Compiler Explorer
我们一起来抽丝剥茧,语法上的一切弯弯绕绕,在CPU汇编指令的面前众生平等。

文章目录

  • 简述volatile概念
  • 案例——不加valatile的情况
    • 案例代码
  • 案例——加上valatile关键字
    • volatile的真正用意
  • volatile的典型应用场景
    • 1.多线程
      • 不加volatile关键字
      • 加上volatile关键字
    • 2.驱动开发
      • 不加volatile关键字
      • 加上volatile关键字
  • 总结

简述volatile概念

volatile可以放到变量的前面,告诉编译器这个变量是易变的、不稳定的。
只能说是一脸懵逼。

案例——不加valatile的情况

本节首先会给一个案例代码,其中包括C++源代码和对应的汇编指令。不懂汇编不要紧,我会进行详细的阐述说明。

首先我们一起探究一下对于一个常规变量,编译器是如何进行处理的。

案例代码

在这里插入图片描述

我们先从CPU的角度理解一下常规变量,然后做一下函数调用:左边CPP源代码的背景色和右边汇编的背景色是一一对应的。
我们对该案例进行以下分析:

  1. while循环体对应的的汇编指令
mov		eax, DWORO PTR a[rip]
cmp		eax, 1
jg		.L2

指令一:读取变量a的值,将a的值(a是全局变量)读取到eax寄存器中。
指令二:比较a和1的大小
指令三:如果a大于1,则跳回,把前面两条指令再做一次。

  1. 提高编译器优化级别
    我们将编译器的优化级别设置为-O2,这通常针对编译过的程序进行性能优化的编译器选项,其目标是提升程序的执行速度,同时尽量保持编译时间和结果程序的大小在合理范围内。
    在这里插入图片描述
    我们整体的代码只上下五条汇编指令!因为编译器会把变量a当作常量来对待。既然a被认为是常量,那就说明a与1比较的结果对于编译器来说是预先可知的,所以我们的汇编竟然直接不执行while循环

这就是编译器在背后为我们做的事情!

案例——加上valatile关键字

如果我们给a加上volatile
其他部分代码都不变,我们在a前面加上volatile关键字,汇编指令如下:
在这里插入图片描述

如上,尽管我们进行了2级优化,但是对于while对应的3条指令编译器并不会把它优化掉了!而是老老实实得从内存中取指令到寄存器,然后寄存器比较寄存器和1的大小,最后跳回或跳出循环。

volatile的真正用意

前文所说:“易变的、不稳定的”的描述其实都是说给编译器听的。

因为编译器会把他认为值不会改变的变量当作常量对待,以此缩减不必要的CPU指令,换取大幅度的效率优化。而volatile就是阻止这种优化,让CPU老老实实从内存中读、写变量

由于编译器的技术进步和各大编译器之间的巨大差异,判定一个变量是否可以被优化,也没有一个统一的标准。这也就是volatile存在的意义!

volatile的典型应用场景

1.多线程

不加volatile关键字

我们在另外一个文件file_B或者另外一个线程task_B中,改变了a的值,让其满足while循环的条件。

//file_A.cpp
int a = 0;
int task_A() {
	while(a > 1) {
		// do something
	}
	return 1;
}

//file_B.cpp
extern int a;
void task_B() {
	a = 2;
}

在这里插入图片描述
我们明明已经在文件B或者线程B的task_B函数更改了,a的值,但是由于编译器将while循环的指令进行了优化,while直接不满足条件而跳出循环!

加上volatile关键字

在这里插入图片描述
加上volatile关键字后,编译器不进行优化,CPU老老实实按照我们的想法来干活。

2.驱动开发

不加volatile关键字

在做驱动开发的时候,我需要通过读一个寄存器来了解USB设备的插拔状态:

unsigned int *REGISTER = (unsigned int *)0xFF001100;

int detect () {
	//initialize register
	*REGISTER = 0xff;
	//read register for USB status
	unsigned int status = *REGISTER;
	return status;
}

在这里插入图片描述
这里经过编译器的代码优化后,尽管将REGISTER寄存器的值乖乖读到rax中,但是我们的unsigned int status = *REGISTER竟然被直接优化了!
因为在编译器眼里,我们就是在读一个毫无变化的值,所以他干脆不读rax寄存器,而是直接把立即数255(0xff)返回。这样我们得到的USB状态永远都是毫无意义的255。

加上volatile关键字

在这里插入图片描述

加上之后,我们的编译器终于开始认认真真的读rax中的寄存器数字了。很舒服

总结

  1. 编译器的代码优化能力:编译器可能对代码中的变量读、写进行适当的优化,避免没有必要的内存读写操作,这往往会大幅度提升程序的执行效率。但当程序变得复杂时,就算编译器也不能完全领会程序员意图,所以这种优化有时候是有害的,需要volatile站出来解决。
  2. volatile关键字到底有什么用:volatile关键字,就是用来避免编译器的优化操作,用来保证每次对变量的读、写都是对内存的真实操作;特别是不会让编译器把某些变量当作常量对待。
  3. 如何判断开不开优化:在编译器不开优化的情况下,很多时候,是否加volatile不会有什么差异,这也让volatile的使用场景变得模糊。判定volatile是否有存在的必要,往往需要查看代码对应的CPU(汇编)指令,看看它是否符合程序员预期。

《CPU眼里的C/C++》的作者阿布大哥在本节最后讲了他的volatile应用场景:
“网卡会把每一个以太网数据包发送两遍。虽然依靠强大的TCP/IP协议的应对能力,这并不会影响网络通信和软件功能。
最后经过排查网卡驱动程序,之所以发送两次,是因为程序判定每次发送以太网包都是不成功的!所以会尝试在发一次。明明成功地发送了,为何被判定为不成功呢?
原来用来标识成功与否的寄存器,他的值被保存在一个变量里面,但由于优化的原因,而该变量被编译器当作常量0来对待了!”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1671984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm+vue的公务用车管理智慧云服务监管平台查询统计(有报告)。Javaee项目,ssm vue前后端分离项目

演示视频: ssmvue的公务用车管理智慧云服务监管平台查询统计(有报告)。Javaee项目,ssm vue前后端分离项目 项目介绍: 采用M(model)V(view)C(controller&…

GCP谷歌云有什么数据库类型,该怎么选择

GCP谷歌云提供的数据库类型主要包括: 关系型数据库:这类数据库适用于结构化数据,通常用于数据结构不经常发生变化的场合。在GCP中,关系型数据库选项包括Cloud SQL和Cloud Spanner。Cloud SQL提供托管的MySQL、PostgreSQL和SQL Se…

高效项目管理:如何利用zz-plan在线甘特图工具

作为项目管理人员,使用 zz-plan https://zz-plan.com/这样的在线甘特图协作软件可以极大地提高项目管理的效率和效果。以下是结合zz-plan特点的一些关键步骤: 1. 制定项目计划 在zz-plan上创建新的项目,定义项目目标、关键里程碑和最终期限。…

【数据可视化01】matplotlib实例介绍2

目录 一、引言二、实例介绍1.箱线图2.热力图3.线条形式 一、引言 接着上一文章【数据可视化01】matplotlib实例介绍1继续介绍matplotlib的实例。 二、实例介绍 在matplotlib中,常用的图形类型包括: 箱线图(Box plot)&#xff1…

d17(154-168)-勇敢开始Java,咖啡拯救人生

目录 方法递归 字符集 编码-解码 IO流 字节流 字节输入流 InputSream FileInputStream 字节输出流 OutputSream FileOutputSream 释放资源的方式 try-catch-finallly try-with-resource 字符流 字符输入流 Reader FileReader 文件字符输出流 Writer FileWriter …

(done) 什么是马尔可夫链?Markov Chain

参考视频:https://www.bilibili.com/video/BV1ko4y1P7Zv/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 如下图所示,马尔可夫链条实际上就是 “状态机”,只不过状态机里不同状态之间的边上是 “…

李廉洋:5.13黄金原油美盘行情分析,必看策略。

黄金消息面分析:机构最新调查中的一些受访者表示,美国最大的科技股不仅是对创新行业的押注,而且可能是对冲通胀的工具。46%的受访者表示,数十年来一直是避险之选的黄金仍被视为抵御价格上涨风险的最佳保障。但近三分之一的人表示&…

【eclipse】如何在IDE里创建一个Java Web项目?

如何在eclipse中创建一个动态Web项目并成功运行? 一、 最终效果 懒得写那么多了…我也不知道该怎么写了,有点乱,有问题可以在评论里留言,我看到会解决的,在这个过程中也踩到了一些坑,但好在有CSDN帮助解决…

GEE数据集——东南亚区域油棕种种植分布(油棕榈树种植园的概率)数据集

森林数据伙伴关系围绕对全球商品驱动的森林砍伐、森林退化和恢复工作的全球监测,加强合作与应用。 世界各国政府和公司都承诺帮助制止砍伐森林和加快恢复,以避免气候变化带来的最坏影响,防止生物多样性丧失,保护森林对人类和自然…

JavaEE之线程(4)——线程安全、线程安全的原因,synchronized关键字

前言 在本栏的前面的内容中,我们介绍了线程的创建、Thread 类及常见方法、线程的状态,今天我们来介绍一下关于线程的另一个重点知识——线程安全。 一、线程安全 基本概念: 线程安全的确切定义是复杂的,但我们可以这样认为&…

微前端无界方案

微前端无界 无界 官方文档 主应用 1、引入 // 无框架时使用wujie import Wujie from wujie // 当结合框架时使用wujie-xxx // import Wujie from "wujie-vue2"; // import Wujie from "wujie-vue3"; // import Wujie from "wujie-react";cons…

想搭建AI知识库的企业看这篇就够了

企业要想在激烈的竞争中脱颖而出,有一套高效、智能的知识管理系统是非常重要的。搭建AI知识库能够帮助企业整合、分类、检索和应用知识,因此成为众多企业的第一选择。对于想要搭建AI知识库的企业来说,应该注意哪些方面呢?本文将从…

专业网站设计方案

当前互联网的快速发展和普及,使得网站设计成为了一个极其重要的环节。一个好的网站设计方案将能够吸引更多的访问者,提高用户体验,增强品牌形象。下面将为您介绍一个专业的网站设计方案。 首先,一个专业的网站设计方案应该具备清晰…

APP反抓包 - 客户端证书验证进阶(代码混淆)

1.关于混淆 在安卓开发中,对于第三方的包是可以进行混淆的,例如:OKHttp3.Http.Cert.check 被混淆后可以是a.f.c.b 形式。在安卓开发中,系统包是无法混淆的,例如:java.security.KeyStore不会被混淆。由于这种的情况的存在,再次审示我们之前的通用脚本,就会发现他是不通用…

2000-2022年上市公司供应链效率数据(含原始数据+结果)

2000-2022年上市公司供应链效率数据(含原始数据结果) 1、时间:2000-2022年 2、指标:年份、股票代码、省份、城市、区县、省份代码、城市代码、区县代码、首次上市年份、上市状态、股票简称、行业名称、行业代码、库存周转率、供…

单页源码加密屋zip文件加密API源码

简介: 单页源码加密屋zip文件加密API源码 api源码里面的参数已改好,往服务器或主机一丢就行,出现不能加密了就是加密次数达到上限了,告诉我在到后台修改加密次数 点击下载

解决宝塔Nginx和phpMyAdmin配置端口冲突问题

问题描述 在对基于宝塔面板的 Nginx 配置文件进行端口修改时,我注意到 phpMyAdmin 的端口配置似乎也随之发生了变化! 解决方法 官方建议在处理 Nginx 配置时,应避免直接修改默认的配置文件,以确保系统的稳定性和简化后续的维护…

过拟合和欠拟合的学习

1.什么拟合 就是说这个曲线能不能很好地描述某些样本数据,并且拥有较好的泛化能力。 2.什么是过拟合 过拟合就是曲线太过于贴切训练数据的特征了,在训练集上表现得非常优秀,近乎完美的预测/区分了所有得数据,但是在新的测试集上…

Springboot整合 Spring Cloud Gateway

1.Gateway介绍 1.是spring cloud官方推出的响应式的API网关框架,旨在为微服务架构提供一种简单有效的API路由的管理方式,并基于Filter的方式提供网关的基本功能,例如:安全认证,监控,限流等等。 2.功能特征…

java图片水印字体乱码问题

问题描述:在linux Centos-7.5_64bit系统的其他服务器上不乱码,在部署项目的正式服务器乱码 水印字体设置是 微软雅黑 Font wordFont new Font("微软雅黑", Font.ITALIC,(srcImgHeightsrcImgWidth)/50); 一.Springboot项目,部署在…