ARM嵌入式编译器-volatile关键字对编译器优化的影响
volatile限定符告知计算机,其他agent(而不是变量所在的程序)可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。要求编译器不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。
使用volatile关键字声明变量,可以让编译器不对该变量进行优化操作。如果在一些需要使用volatile关键字的地方而没有使用它时,那么编译器可能会优化对该变量的访问,并生成意外的代码或达不到预期的功能。
目录
1 volatile意味着什么
2 什么时候该使用volatile
3 不使用volatile可能出现的问题
4 强制使用特定指令来访问内存
5 使用volatile和不使用volatile的汇编对比
6 总结
编译器会优化什么
作用
特点
1 volatile意味着什么
将变量声明为volatile,是在告诉编译器,该变量的值可能随时会被其他实体(比如操作系统或者硬件)改变。该声明可以保证编译器不会优化对该变量的使用,即使该变量未被使用或者未被更改。
2 什么时候该使用volatile
对于可能在定义它们的作用域之外被修改的变量,使用volatile关键字。比如:
- 会被多个子程序读写的全局变量。如果程序在某些计算中使用到了全局变量,由于需要经常对该变量进行读写操作,编译器为了加速读写操作,会生成代码将该变量的值加载到寄存器中,以执行该计算。如果相同的全局变量随后在另一个子程序中被使用,编译器可能会直接读取寄存器中的现有值,而不是去内存当中加载(寄存器中的是最新的值,还没有更新到内存中,而该子程序的本意是使用内存中的值),这可能导致计算错误。这种直接读取寄存器里的值的做法,是因为优化器认为该变量是non-volatile的,不能从外部被修改,而这种假设对于内存映射的外设是不正确的。
- 被用作定时(sleep或者timer)函数的延时变量。如果这种变量从未被使用过,编译器可能会将整个延时函数代码移除,除非将该变量声明为volatile。
- 被中断服务子程序调用的变量。如下示例代码中,中断子程序async_interrupt在类中声明定义,但是它可能被硬件异步调用。被调用后会改变一个名为buffer_full的变量,如果不把buffer_full声明为volatile,check_stream函数可能会读不到buffer_full被async_interrupt更改后的值,导致程序错误。
-
class myclass {public:int check_stream();void async_interrupt();private:bool buffer_full; // must be declared as volatile }; int myclass::check_stream() {int count = 0;while (!buffer_full){count++;}return count; } void myclass::async_interrupt() {buffer_full = !buffer_full; }
-
此外:
- 当访问一些内存映射的外设时,必须使用volatile关键字声明。就算编译时采用 -O0(默认不优化),仍然不能保证每个变量都被当作volatile。
volatile
并不意味着内部线程通信或者同步(inter-thread communication or synchronization),要达到这个目的,需要使用原子性操作,而不是volatile。- 中断或者信号处理器必须使用原子性或者volatile sig_atomic_t 类型的变量,但不是任意类型的volatile变量,以确保多线程执行下的同步。
- 在内联汇编代码前,也可以使用volatile修饰。
3 不使用volatile可能出现的问题
如果需要使用volatile的时候而没有使用,编译器就会认为该变量不会被当前作用域外的其他实体修改。因此,编译器可能会进行一些用户意想不到的优化操作,可能会导致:
- 在轮询硬件时,代码可能会陷入循环。
- 优化可能会导致删除实现故意定时延迟的代码。
4 强制使用特定指令来访问内存
使用volatile关键字声明变量,并不能保证使用特定的机器指令来访问它。比如Cortex®-R7 and Cortex-R8的AXI外设端口是一个64-bit的外设寄存器。这个寄存器必须使用STM(多寄存写入)来写入,而不是STRD或者一对STR指令。不能保证编译器在响应相关变量或指针类型上的volatile修饰时选择该寄存器所需的访问方法。因此还可以使用volatile关键字来修饰内联汇编代码。
__asm__ volatile("stm %1,{%Q0,%R0}" : : "r"(val), "r"(ptr));
__asm__ volatile("ldm %1,{%Q0,%R0}" : "=r"(val) : "r"(ptr));
5 使用volatile和不使用volatile的汇编对比
有如下两段代码:
test1:
int buffer_full;
int read_stream(void)
{int count = 0;while (!buffer_full){count++;}return count;
}
test2:
volatile int buffer_full;
int read_stream(void)
{int count = 0;while (!buffer_full){count++;}return count;
}
test1和test2唯一的区别就是buffer_full变量是否被声明为volatile。buffer_full是一个结束while循环的flag,当buffer_full==true时,停止计数,跳出循环并返回count。我们先看编译结果,再进行分析:
使用 armclang --target=arm-arm-none-eabi -march=armv8-a -Os -S 命令对这两段程序进行编译:
test1:
1 read_stream:
2 movw r0, :lower16:buffer_full
3 movt r0, :upper16:buffer_full
4 ldr r1, [r0]
5 mvn r0, #0
6 .LBB0_1:
7 add r0, r0, #1
8 cmp r1, #0
9 beq .LBB0_1 ; infinite loop
10 bx lr
test2:
1 read_stream:
2 movw r1, :lower16:buffer_full
3 mvn r0, #0
4 movt r1, :upper16:buffer_full
5 .LBB1_1:
6 ldr r2, [r1] ; buffer_full
7 add r0, r0, #1
8 cmp r2, #0
9 beq .LBB1_1
10 bx lr
先看test1的汇编程序,第6行到第9行为while循环语句,寄存器r1里的值为buffer_full,进入.LBB0_1的循环后,通过第8行的cmp指令不断比较r1是否为0,以此判断是否要跳出循环。由于test1没有使用volatile关键字声明变量buffer_full,所以在.LBB0_1的循环内一直读取的是寄存器r1的值,如果在轮询的过程中,有其他子程序改变了buffer_full的值,test1也不会跳出循环,因为它并不知道buffer_full已经被更新了。
而在test2程序中,寄存器r1中存储的是buffer_full的地址,r2中存储的是它的值。由于使用了volatile声明,编译器认为该变量是易变的,有被其他子程序更改的风险,所有在第5行到第9行的循环中,每次读取buffer_full的值都是使用 ldr r2, [r1] ,直接加载内存当中的值,而不是如test1中的,读取寄存器的值。所以当外部程序更改了buffer_full的值,test2就能立马读取到真实的buffer_full,从而跳出循环。
6 总结
智能的(进行优化的)编译器可能会把变量的值临时储存在寄存器上,便于下次读取,以节约时间,这个过程被称为高速缓存。但是有一些agent在内存上改变了变量的值,寄存器上的还是旧数据,这样就出错了。如果被volatile 关键字修饰,编译器不会进行高速缓存,直接去内存中读取该变量的数据。
编译器会优化什么
- 将内存变量缓存到寄存器中。
- 调整指令顺序,充分利用CPU指令流水线,进行指令重新排序读写指令。
作用
告诉编译器该变量值是不稳定的,可能被更改,需要去内存中读取该值而不是读取寄存器中的备份
- 多个线程都要用到的某个变量,而且变量的值会被改变
- 中断服务子程序中访问到的非自动变量
- 并行设备硬件寄存器的变量(如状态寄存器)
特点
- 易变的
- 不可优化,告诉编译器不要对volatile声明的变量进行各种优化保证程序员写在代码中的指令一定会被执行
- volatile int a;a = 1; 如果未声明为volatile两条代码会合并成一条。
- 顺序执行的(原子性):保证volatile变量间的顺序性,不会被编译器进行乱序优化
- 能否和const一起用:可以,const是只读,volatile是去内存中读取
- 指针可以是volatile
- 可以修饰函数参数
参考文章:
Effect of the volatile keyword on compiler optimizationhttps://developer.arm.com/documentation/100748/0620/Writing-Optimized-Code/Effect-of-the-volatile-keyword-on-compiler-optimization?lang=en#a48-effect-of-the-volatile-keyword-on-compiler-optimization__c-code-for-nonvolatile-and-volatile-buffer-loops
相关文章:
ARM嵌入式编译器-volatile关键字对编译器优化的影响
volatile限定符告知计算机,其他agent(而不是变量所在的程序)可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。要求编译器不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。 使用…...
销售数据分析怎么做?这篇文章说清楚了
如何分析销售数据?分析销售数据有哪些指标?销售数据分析有什么作用? 销售数据是不是得通过数据分析软件啊? 本文将为您解答疑惑—— 一、分析销售数据的指标 从两个层面上来讲,一个是对销售情况的整体把控…...
二十六、ISIS技术总结
文章目录 ISIS 概述一、路由协议总结1、路由优先级2、分类 二、ISIS 协议特点1、特点2、ISIS 路由器的种类 三、ISIS 配置1、基础配置2、network-entity含义3、router id 和系统id转换规则 四、ISIS 开销计算1、Narrow 模式2、Wide 模式 五、 ISIS 和 OSPF 的区别 ISIS 概述 I…...
三菱m70 m80系统解密 三菱m80机床到期解锁
我们从操作系统的发展讲起,为什么要有线程这个概念出现。《Java多线程学习笔记(一) 初遇篇》讲Java平台下的线程,如何使用和创建,以及引入线程后所面临的问题,为了解决线程安全问题,Java引入的机制,这也是《…...
InnoDB 磁盘结构之数据字典和双写缓冲区
数据字典(InnoDB Data Dictionary) MySQL中,数据字典包括了: 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、MySQL版本信息、存储过程、触发器等内容 InnoDB数据字典由内部系统表组成,这些表包含用于查找表…...
Django模型层part two - 多表关系创建和多表操作
前言 继续上面一篇文章的内容,本文介绍多表操作。使用django ORM可以创建多表关系,并且也支持多张表之间的操作,以创建表关系和查询两部分说明django ORM的多表操作。以作者、图书、出版社和作者信息几张表作为案例进行说明。 创建表关系 …...
智能优化算法:浣熊优化算法-附代码
智能优化算法:浣熊优化算法 文章目录 智能优化算法:浣熊优化算法1.浣熊优化算法1.1 初始化1.2 阶段一:狩猎和攻击(探索阶段) 2.实验结果3.参考文献4. Matlab 摘要:浣熊优化算法(Coati Optimizat…...
【51单片机】数码管显示(样例展示以及异常分析)
🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐数码管 比如要显示“6”,那么下面图片中,AFEDCG=1,B=0 对应到数码管上,就是 ⭐原理 🎊P22~P24控制LED1~...
Android InputChannel事件发送接收系统分析
本文基于Android12。 InputChannel表示其他进程通过文件描述符传递输入事件到View的通道,因为需要跨进程传输,实现了Parcelable序列化接口,所以也能够理解Java层的InputChannel后面为什么使用copyTo()方法初始化。 输入事件的接收方是View&…...
Java时间类(五)-- LocalDate()类
目录 引言: 1. LocalDate的概述: 2. LocalDate的常用方法: 引言: (1)Date存在的缺陷: 如果不格式化,打印出的日期可读性差://获取当前时间Date date = new Date();System.out.println("date = " + date); //date = Wed May 03 22:30:24 CST...
用手机号码归属地 API 开发的应用推荐
引言 手机号码归属地 API是一种提供手机号码归属地信息的接口,通过该接口,可以获取手机号码所属的省份、城市、运营商等信息。它可以帮助企业更好地了解客户,为个性化推荐和精准广告投放提供数据支持。作为一种数据服务,手机号码…...
测试从业第 3 年,我看到了终点......
先说明,今天的内容,是写给想成为高级测试开发、自动化测试专家的人看的,因为,它可能颠覆你的认知。 众所周知,如今无论是大厂还是中小厂,自动化测试基本是标配了,毕竟像双11、618 这种活动中庞…...
结巴分词原理分析
结巴分词器工作原理 结巴分词是一款python写成的开源中文分词器,分词过程大致如下: 首先,结巴使用正则表达式将输入文本切割成若干中文块,接着对每个中文块B做处理,将B转成有向无环图(DAG)。DAG是以{key:list[i,j...…...
JavaEE 第三-四周
计算机Z20-第3-4周作业 总分:100分 得分:74.2分 1 . 填空题 简单 5分 在web.xml文件中,<url-pattern>/xxxxServlet</url-pattern>中的第一个‘/’表示__________。 学生答案 当前web应用程序的根目录 2 . 填空题 简…...
Ububtu20.04 无法连接外屏(显卡驱动问题导致)
Ububtu20.04 无法显示第二个屏幕(显卡驱动问题) Ububtu20.04 无法显示第二个屏幕(显卡驱动问题) Ububtu20.04 无法显示第二个屏幕(显卡驱动问题) 1. 问题描述2. 解决方案 1. 问题描述 一开始我的ububt…...
配置JDK环境变量
文章目录 查看电脑系统下载及安装JavaSE配置系统环境变量测试环境变量配置是否成功。 查看电脑系统 运行输入框中输入:control 下载及安装JavaSE 这个从网上下载就行,jdk-8u141-windows-x64.exe,不提供下载方式了。 主要讲解安装过程&a…...
保护移动设备免受恶意软件侵害优秀方法
几天前,移动恶意软件攻击增加了500%显然,我们大多数人都不知道不能很好地保护我们的手机下面小编揭秘有效保护移动设备免受恶意软件侵害的最佳方法。 1、使用移动反恶意软件 恶意软件很容易感染智能手机和平板电脑,因此在设备上安装可靠的…...
一个人在家怎么赚钱?普通人如何通过网络实现在家就能赚钱
近年来,随着互联网的飞速发展,嗅觉敏锐的人只要使用互联网就可以快乐地赚钱。一般来说,网上赚钱的投资较少,有时有一台能上网的电脑或手机就够了,所以大家有时称其为“无成本或低成本网赚”。今天就分享一个人在家如何…...
ChatGPT诞生的新岗位:提示工程师(Prompt Engineer)
ChatGPT诞生的新岗位:提示工程师(Prompt Engineer) Prompt 工程师是什么? 是识别人工智能的错误和隐藏功能,以便开发者可以对这些发现进行处理。 如果你正在寻找科技领域最热门的工作,你可以尝试了解如何与AI聊天机…...
机器学习笔记 使用PPOCRLabel标注自己的OCR数据集
一、PPOCRLabel的安装 最简单的方式就是直接pip安装,如下命令。 pip install PPOCRLabel -i https://pypi.douban.com/simple/ 运行的时候,直接激活安装了PPOCRLabel的环境后,输入PPOCRLabel回车即可运行,不过PPOCRLabel依赖PyQt5,所以会要求安装PyQt5,按要求安装或者提前…...
【C++初阶】类和对象(二)
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C初阶 🎯长路漫漫浩浩,万事皆有期待 上一篇博客:【C初阶】…...
深入探讨Java、Spring和Dubbo的SPI机制
在Java开发领域中,SPI(Service Provider Interface)是一种用于实现框架扩展的机制。Java本身提供了SPI机制,Spring和Dubbo也都有自己的SPI机制。本文将介绍Java、Spring、Dubbo三者SPI机制的原理和区别。 一、Java SPI机制 Java…...
使用机器人为无线传感器网络提供服务(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 无线传感器网络是一种无线网络,包括大量循环的、自定向的、微小的、低功耗的设备,称为传感器节点&…...
QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样
QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样 [1] QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样一、本自制虚拟键盘特点二、windows打开系统自带软键盘三、让键盘界面保持在最上方、不改变底层界面焦点四、长按按键重复输入键盘内容五、模拟键盘点击事件完成虚拟键盘…...
优思学院|8D和DMAIC两种方法应如何选择?
在现代的商业环境中,客户投诉是一个非常常见的问题。当客户不满意产品或服务时,他们往往会向企业发出投诉。质量管理部门是一个负责处理这些投诉的重要部门,因为它们需要确保产品和服务的质量满足客户的期望。改善方法是质量管理部门用来解决…...
回归预测 | MATLAB实现MLR多元线性回归预测(多指标评价)
回归预测 | MATLAB实现MLR多元线性回归预测(多指标评价) 目录 回归预测 | MATLAB实现MLR多元线性回归预测(多指标评价)预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 回归预测 | MATLAB实现MLR多元线性回归预测(多指标评价) 模型描述 多元线性回归(Multip…...
PHP 二维数组相关函数:二维数组指定key排序,二维数组转一维数组,两个二维数组取差集,对象转数组,判断元素是否在多维数组中
目录 一、二维数组转一维数组 二、对二维数组进行指定key排序 三、二维数组转一维数组 四、两个二维数组取差集 五、对象转数组 六、判断元素是否在多维数组中 PHP 二维数组相关函数:二维数组转一维数组,二维数组指定key排序,两个二维数…...
演出剧院门票售票预约小程序开发制作功能介绍
基于微信小程序的票务预约小程序,广泛适用于演出主办方、剧院、艺术中心、活动中心售票、景区门票售票、儿童游乐园售票、会务签到、展会售票签到、教育培训报名预约、健身预约功能。 多场景售票支持: 售票软件支持多种场景的售票,支持选座、…...
JUC之Java内置锁的核心原理
文章目录 JUC之Java内置锁的核心原理Java对象结构对象头对象体对齐字节 Mark Word的结构信息64位Mark Word的构成 偏向锁偏向锁的设置偏向锁的重偏向偏向锁的撤销偏向锁的膨胀 轻量级锁执行过程轻量级锁的分类普通自旋锁自适应自旋锁 重量级锁偏向锁、轻量级锁与重量级锁的对比…...
【项目经理】论项目经理的自我修养
项目经理的非职权领导力 文章目录 项目经理的非职权领导力一、权利的类型二、构成权利的三要素三、沟通是实施影响力的重要手段3.1 沟通的主要类型3.2 沟通的内容和形式3.3 沟通的主要困难 四、综合沟通协调的技巧4.1 常见的负面反馈4.2 沟通技巧 五、论项目经理的自我修养5.1 …...
百度右侧相关网站/全球最牛的搜索引擎
2019独角兽企业重金招聘Python工程师标准>>> 一、jQuery fadeIn() 用于淡入已隐藏的元素。 <!DOCTYPE html> <html> <head> <script src"/jquery/jquery-1.11.1.min.js"></script> <script> $(document).ready(func…...
编程做网站容易还是做软件/龙斗seo博客
2019独角兽企业重金招聘Python工程师标准>>> redis就是叫redis memcached的客户端dalli sinatramongoidmysqlsidekiqrails_admindeviseCMS(LocomotiveCms) 转载于:https://my.oschina.net/u/934148/blog/495720...
wordpress仿百度贴吧/网站设计公司多少钱
.NET是一个微软开发的编程环境,里面可以使用C#,VB等多种编程语言。 不叫点net哦,叫点net太业余了啊。。。 显得太业余了。之前一直在用,today才know规范的叫法,,,。过去真是草莽写代码模式的了。 1 基础概…...
济南外贸网站建设公司排名/在线识图
计算机网络课程设计某大学校园网设计_1摘要当今世界,各种先进的科学技术飞速发展,给人们的生活带来了深远的影响,它极大地改善着我们的生活方式。在以计算机技术为代表的信息科技的发展更是日新月异,从各个方面影响和改变着我们的…...
一个网站如何做推广/seo优化入门教程
从MySQL5.6开始,mysqlbinlog支持将远程服务器上的binlog实时复制到本地服务器上。mysqlbinlog的实时二进制复制功能并非简单的将远程服务器的日志复制过来,它是通过MySQL 5.6公布的Replication API实时获取二进制事件。本质上,就相当于MySQL的…...
首钢建设二建设公司网站/百度拍照搜索
指导学生参加计算机技能大赛实践总结及思索指导学生参加计算机技能大赛实践总结及思索摘要:通过参加两届“全国高职高专院校计算机综合应用能力大赛”现实经历,深感大赛对对提高学生计算机应用能力,推动计算机基础课程教学改革的强大作用。赛…...