从汇编解释volatile关键字的意义

两个示例函数,循环对一个全局变量(c和d)进行累加计算,其中变量c有volatile关键字修饰,变量c没有。使用gcc O2优化编译后,使用objdump导出汇编。

从汇编代码看出,add函数被编译优化,内存变量d首先被放到了寄存器EAX,后续所有的累加操作,都是对EAX中值的累加,循环计算完成后,最后EAX结果写入内存变量d()。

addx函数没有被优化,循环中每一次累加前从内存变量c读取写入EAX,计算后再都被写入内存变量c(11c0),再从内存变量C读出放入EAX,做下一步循环计算。

两者比较之下,add对内存变量d的读写各只有1次,addx则在每次循环都有一次对变量c的读写操作,显然addx的性能会因此差很多。

但在多线程程序中,在循环累加的过程中,如果有另一个线程读取变量c和d,那么对于c变量可以获取到每一步循环的累加的中间值。而对于变量d,只会获取到初始值,直到add循环结束后才能获取到最终结果。

因此,在多线程并发程序中,共享变量最好加上volatile,特别是当这个变量在一个线程的逻辑过程中会多次变更,而另一个线程需要获取这个变量的实时状态时,否者可能只会获取到的该变量值可能已经过期。如果对于共享变量值的实时性并没有要求,则volatile并非必须。

volatile int c = 0;
int d = 0;

void add(int a, int b ) {
    while ( d < 100 ) d = d + a + b;
}

void addx(int a, int b ) {
    while ( c < 100 ) c = c + a + b;
}
0000000000001190 <add>:
    1190:       endbr64
    1194:       mov    0x2e7a(%rip),%eax        # 4014 <d>
    119a:       cmp    $0x63,%eax
    119d:       jg     11af <add+0x1f>
    119f:       nop
    11a0:       add    %esi,%eax
    11a2:       add    %edi,%eax
    11a4:       cmp    $0x63,%eax
    11a7:       jle    11a0 <add+0x10>
    11a9:       mov    %eax,0x2e65(%rip)        # 4014 <d>
    11af:       retq

00000000000011b0 <addx>:
    11b0:       endbr64
    11b4:       jmp    11d0 <addx+0x20>
    11b6:       nopw   %cs:0x0(%rax,%rax,1)
    11bd:       00 00 00
    11c0:       mov    0x2e52(%rip),%eax        # 4018 <c>
    11c6:       add    %edi,%eax
    11c8:       add    %esi,%eax
    11ca:       mov    %eax,0x2e48(%rip)        # 4018 <c>
    11d0:       mov    0x2e42(%rip),%eax        # 4018 <c>
    11d6:       cmp    $0x63,%eax
    11d9:       jle    11c0 <addx+0x10>
    11db:       retq
    11dc:       nopl   0x0(%rax)

上一篇:比较地址


下一篇:Mac安装和破解激活Charles