鲲鹏社区首页
中文
注册
开发者
我要评分
获取效率
正确性
完整性
易理解
在线提单
论坛求助

慎用volatile修饰全局变量

【说明】 全局变量尽量不使用volatile修饰 ,以便编译器优化汇编代码提高性能。

【原理】 volatile关键字用以提醒编译器,其修饰的变量随时有可能被改变,需要保证编译后的程序每次需要存储或读取这个变量的时候,都需要直接访问其变量地址。

从本质上讲,volatile关键字的目的是抑制编译器的编译期优化:如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

为了避免编译器优化时将变量先读取到寄存器,而后直接从寄存器取值,而忽略了其他线程更改此变量时的场景。解释为“直接存取原始内存地址”更合适。一般来说,通过mmap技术将外部寄存器对象映射到内存地址的场景下,外部寄存器可能被硬件修改,此时需要增加volatile关键字修饰;

【注意事项】

  • 针对外部寄存器、跨线程共享变量如果误删除其volatile关键字可能导致编译器误优化,从而引入功能问题;
  • 采用volatile关键字修饰变量,不是一种线程间同步的技术,无法做到严格的同步保证(尤其是对于弱一致性的缓存模型);因此对于多线程并发同步、或处理器乱序带来等场景,需要针对性地选择线程同步、内存屏障等技术手段语语义解决;
【案例】(需要使用volatile关键字修饰的场景)
int Func()
{
    char *reg =  (char *)0x1234;
    while(*reg != 0){
    }
    return 0;
}
对应的汇编代码为:
Func():
        mov     r3, #4608
        ldrb    r0, [r3, #52]   @ zero_extendqisi2
.L2:
        cmp     r0, #0
        bxeq    lr
        cmp     r0, #0
        bne     .L2
        bx      lr

更换成volatile关键字修饰之后,

int Func()
{
    char volatile *reg = (char *)0x1234;
    while(*reg != 0){
    }
    return 0;
}
对应的汇编代码为:
Func():
        mov     r3, #4608
.L2:
        ldrb    r0, [r3, #52]   @ zero_extendqisi2
        ands    r0, r0, #255
        bne     .L2
        bx      lr

上述案例能够看到,增加volatile修饰,可以避免编译器对变量的读取过程进行优化,从而导致功能异常的问题;除了上述需要读取外部寄存器等场景外,不建议volatile关键字修饰。