本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。
博客内容主要围绕:
5G/6G协议讲解
算力网络讲解(云计算,边缘计算,端计算)
高级C语言讲解
Rust语言讲解
文章目录
- 一、从汇编代码看volatile关键字的作用
- 1.1 volatile关键字告诉编译器不要优化我的代码
- 1.2 volatile关键字告诉编译器从内存读取变量的值
- 二、总结
一、从汇编代码看volatile关键字的作用
正文开始之前先给大家介绍一款可以实时将代码翻译为汇编语言的工具
Compiler Explorer
传送门
你可以选择程序语言和编译器,不同的编译器生成的汇编语言可能有较大差异。将鼠标放到汇编语句上,会同步显示左边语言对应的语句,非常方便汇编语言的学习以及深入理解编程语言背后的秘密。
1.1 volatile关键字告诉编译器不要优化我的代码
我们编写一个测试用例,定义一个全局变量a,然后做函数调用。如下所示:
int a=0;
int test_a()
{
while(a>1)
{
// do something
}
return 1;
}
将其输入到Compiler Explorer中,对应的汇编代码如下:
- 汇编第8行语句读取变量a的值;
- 汇编第9行比较a与1的大小;
- 汇编第10行如果a大于1,则跳回while语句;
上面的代码没有任何问题,下面我们将优化等级调整到-O2
看看有什么变化:
可以看到开启优化之后,代码量减少了,甚至直接将while循环优化掉直接返回1了。这是因为编译器将变量a视为常量,既然是常量则变量a与1比较的结果就是预先可知的;while循环条件不满足,显然不需要执行,就被编译器优化掉了。下面我们给变量a添加volatile关键字,再看看效果如何。
volatile int a=0;
int test_a()
{
while(a>1)
{
// do something
}
return 1;
}
可以看到即使开启了优化,while循环还是存在的没有被优化掉。这是因为编译器会把它认为不会改变的变量当作常量对待,这样可以减少CPU的指令数,而volatile就是阻止这种优化的,让CPU老老实实的从内存中读取、写变量。
1.2 volatile关键字告诉编译器从内存读取变量的值
我们再写另一个例子,存在一个文件file_b,其会改变上面例子中的变量a的值,使其等于3,这样就满足while循环条件了。相关代码如下:
// file_a.cpp
int a=0;
int test_a()
{
while(a>1)
{
// do something
}
return 1;
}
// file_b.cpp
extern int a;
void test_b()
{
a=3;
}
如你所见,此时编译器还是将变量a视为常量0,进而优化掉整个while循环,这就非常不合理了!例如,再做驱动开发的时候,你需要通过读取一个寄存器来了解USB设备的插拔状态,例如下面的代码:
可以看到编译器优化后的代码,根本没有去读寄存器,而是直接将立即数255返回给你,这样你的USB状态始终都是255。为了防止编译器把变量视为常量一样优化,就必须使用volatile关键字,例如下面的代码:
二、总结
- 编译器可能对代码中的变量读、写进行适当的优化,避免没有必要的内存读写操作,这往往会大幅度提升程序的执行效率。但编译器也是程序,只能针对特定情况做特定的优化,当程序变得复杂时,编译器也未必能完全领会程序员的意图,所以,有些时候这种优化是有害的;
- volatile关键字,就是能用于避免编译器优化的关键字,用来保证每次变量的读写都是针对内存进行的;特别是不会让编译器把某些变量当作常量来对待;
- 在编译器不开优化的情况下,很多时候,是否加volatile,不会有任何差异。这也让volatile的使用场景变得十分模糊。判定volatile是否有必要存在,往往需要查看代码对应的CPU指令,看看它是否符合程序员的预期。
这里是从善若水的博客,感谢您的阅读📕📕📕