使用内存屏障强制按顺序执行
试着继续我的想法,使用软件和硬件内存障碍我可以禁用编译器优化编译的代码中的特定函数的无序优化,因此我可以使用像Peterson
这样的算法实现软件信号量或者Deker
不需要无序执行,我测试了以下代码,其中包含SW barrier asm volatile("": : :"memory")
__sync_synchronize
asm volatile("": : :"memory")
和gcc builtin HW barrier __sync_synchronize
:
#include 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
。
根据相关的StackOverflowpost –
gcc内存屏障__sync_synchronize vs asm volatile(“”:::“memory”)
在每次迭代时添加内联汇编时,不允许gcc更改通过屏障的操作顺序
后来:
但是,当CPU执行此代码时,只要它不破坏内存排序模型,就允许对“引擎盖下”的操作进行重新排序。 这意味着执行操作可以不按顺序进行(如果CPU支持这些操作,就像大多数情况那样)。 硬盘围栏会阻止它。
但是正如你所看到的,代码与内存屏障和没有内存屏障的代码之间的唯一区别是前一个包含mfence
的方式我不希望看到它,并且不包括所有的赋值。
为什么带有内存屏障的文件的输出文件不符合我的预期 – 为什么mfence
顺序已被更改? 为什么编译器会删除一些分配? 即使应用了内存屏障,编译器是否允许进行此类优化并分离每一行代码?
引用内存屏障类型和用法:
-
内存障碍 – http://bruceblinn.com/linuxinfo/MemoryBarriers.html
-
GCC Builtins – https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html
内存屏障告诉编译器/ CPU不应该跨屏障重新排序指令,它们并不意味着无论如何都必须完成无可争议的写入。
如果将x
定义为volatile
,则编译器无法做出假设,它是唯一关心x
s值的实体,并且必须遵循C抽象机器的规则,这是实际发生的内存写入。
在您的特定情况下,您可以跳过障碍,因为已经保证不会相互重新排序易失性访问。
如果你有C11支持,最好使用_Atomic
s,这还可以保证正常的赋值不会对你的x
重新排序,并且访问是primefaces的。
编辑:GCC(以及clang)似乎在这方面不一致,并不总是做这种优化。 我打开了关于此的GCC错误报告。