volatile关键字使用总结
  MYxncHy1f6BF 2023年11月02日 56 0


先说结论

1. volatile关键字可以让编译器层面减少优化,每次使用时必须从内存中取数据,而不是从cpu缓存或寄存器中获取

2. volatile关键字不能完全禁止指令重排,准确地说是两个volatile修饰的变量之间的命令不会进行指令重排

3. 使用volatile可以解决一部分的线程并发问题,但是不能解决所有的并发问题

4. volatile只能让编译器不做指令重排的优化,但是cpu层面的指令重排仍然不受影响。如果想禁止cpu的指令重排,可以使用__sync_synchronize()

实验一:volatile修饰的变量会让编译器减少优化,每次使用时必须从内存中取数据

代码如下:

const auto start = system_clock::now();
int i = 0;
while (i<50000000)
{
    i++;
}
cout << duration_cast<milliseconds>(system_clock::now() - start).count() << endl;

代码很简单,就是把变量i自增5000万次,计算这个过程用的毫秒数

debug模式下,打印结果如下:

volatile关键字使用总结_汇编指令

release模式下,打印结果如下:

volatile关键字使用总结_汇编指令_02

可以看到,同样的代码,

debug模式下耗时15毫秒

release模式下竟然耗时0毫秒,也就是程序根本就没执行5000万次自增,因为编译器发现5000万次自增后面压根没用到变量i,这些操作等于是无意义的,所以自作主张地跳过了5000万次自增对应的编译命令

实验二:volatile并不能完全禁用编译器的指令重排

代码如下:

int a = 0;
int b = 0;
void foo(void)
{
    a = b + 1;
    b = 2;
}

debug模式下,我们打上断点,并打开反汇编窗口查看对应的汇编指令,可以看到如下图

volatile关键字使用总结_volatile关键字_03

a = b + 1对应3个汇编指令

b = 2 对应1个汇编指令

完全符合我们的预期

改为release模式,我们打开反汇编查看对应更大汇编指令,如下图

volatile关键字使用总结_汇编指令_04

可以发现

第二句代码"b=2"的汇编指令居然在前面

第一句代码"a = b+1"的汇编指令反而在后面,且经过了变形

说明,编译器帮我们进行了优化

实验二 - 优化一

我们给变量a加上volatile,如下

volatile int a = 0;
int b = 0;
void foo(void)
{
    a = b + 1;
    b = 2;
}

release模式下,再次查看反汇编,如下图

volatile关键字使用总结_汇编指令_05

可以看到,给变量a使用volatile关键字修饰后,编译器虽然强制从内存取了一次数据,但仍然进行了指令优化,比如先把1赋给了变量a,然后才对变量b的寄存器执行xor操作,仍然不是最初的汇编指令。

实验二 - 优化二

我们给变量a,变量b都加上volatile,如下

volatile int a = 0;
volatile int b = 0;
void foo(void)
{
    a = b + 1;
    b = 2;
}

在release模式下,再查看反汇编窗口,如下图,总算符合我们的预期了

volatile关键字使用总结_volatile关键字_06

说明,单个volatile并不能完全阻止编译器对指令进行重排优化,两个volataile共同作用才能阻止编译器对指令进行重排优化

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
MYxncHy1f6BF