尝试继续我的想法,使用软件和硬件内存障碍我可以禁用编译器优化编译的代码中的特定函数的无序优化,因此我可以使用软件信号量实现像Peterson
或Deker
这样的算法不需要无序执行,我测试了以下包含SW barrier asm volatile("": : :"memory")
和gcc builtin HW barrier __sync_synchronize
的代码:
#include <stdio.h>
int main(int argc, char ** argv)
{
int x=0;
asm volatile("": : :"memory");
__sync_synchronize();
x=1;
asm volatile("": : :"memory");
__sync_synchronize();
x=2;
asm volatile("": : :"memory");
__sync_synchronize();
x=3;
printf("%d",x);
return 0;
}
但编译输出文件是:
main:
.LFB24:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
mfence
mfence
movl $3, %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
mfence
call __printf_chk
xorl %eax, %eax
addq $8, %rsp
如果我删除障碍并再次编译,我会得到:
main
.LFB24:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $3, %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
call __printf_chk
xorl %eax, %eax
addq $8, %rsp
在Ubuntu 14.04.1 LTS,x86中使用gcc -Wall -O2
编译。
预期的结果是包含内存屏障的代码的输出文件将包含我在源代码中的值的所有赋值,它们之间有mfence
。
根据相关的StackOverflow帖子 -
gcc memory barrier __sync_synchronize vs asm volatile("": : :"memory")
在每次迭代时添加内联汇编时,不允许gcc更改通过障碍的操作顺序
后来:
但是,当CPU执行此代码时,允许重新排序 操作“引擎盖下”,只要它不破坏记忆 订购模式。这意味着可以执行操作 乱序(如果CPU支持,就像大多数人这样做)。一个HW 栅栏会阻止它。
但正如您所看到的,代码与内存屏障和没有内存屏障的代码之间的唯一区别在于前者包含mfence
的方式我不希望看到它,而不是所有的包括作业。
为什么带有内存屏障的文件的输出文件不符合我的预期 - 为什么mfence
订单已被更改?为什么编译器会删除一些赋值?即使应用了内存屏障,编译器是否允许进行此类优化并分离每一行代码?
引用内存屏障类型和用法:
答案 0 :(得分:4)
内存屏障告诉编译器/ CPU不应该跨屏障重新排序指令,它们并不意味着无论如何都必须完成无证据的写入。
如果您将x
定义为volatile
,则编译器无法做出假设,它是唯一关心x
值并且必须遵循以下规则的实体C抽象机器,用于实际发生的内存写入。
在您的特定情况下,您可以跳过障碍,因为它已经保证不会相互重新排序易失性访问。
如果您有C11支持,最好使用_Atomic
,这样可以保证不会对您的x
重新排序正常分配,并且访问是原子的。