试图继续我的想法,使用软硬件内存屏障,我可以禁用在编译优化编译的代码中的某个特定函数的无序优化,因此我可以使用不需要无序执行的算法(如Peterson或Deker )来实现软件信号量,我已经测试了以下代码,该代码同时包含SW屏障asm volatile("": : :"memory")和gcc内置的HW屏障__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两者都是用gcc -Wall -O2在Ubuntu14.04.1LTS,x86中编译的。
预期的结果是,包含内存屏障的代码的输出文件将包含我在源代码中的所有赋值,其中包含mfence。
根据一个相关的StackOverflow帖子-
在每次迭代中添加内联程序集时,gcc不允许更改跨越障碍的操作顺序。
后来:
但是,当CPU执行此代码时,只要它不破坏内存排序模型,就允许重新排序“幕后”的操作。这意味着可以按顺序执行操作(如果CPU支持这种操作,就像现在大多数支持的那样)。HW围栏会阻止这种情况的。
但是,正如您所看到的,带内存屏障的代码和没有内存障碍的代码之间唯一的区别是,前者包含mfence的方式是我没有预料到的,而且不是所有的赋值都包括在内。
为什么带有内存屏障的文件的输出文件不像我预期的那样--为什么mfence顺序被改变了?为什么编译器要删除一些赋值?即使应用了内存屏障并将每一行代码分开,编译器是否允许进行这样的优化?
对内存屏障类型和用法的引用:
发布于 2016-08-03 11:05:56
内存屏障告诉编译器/CPU,指令不应该跨障碍重新排序,它们并不意味着无论如何都必须完成可以证明毫无意义的写入。
如果将x定义为volatile,编译器就不能假设它是唯一关心x值的实体,必须遵循C抽象机器的规则,这是内存写入实际发生的条件。
在您的具体情况下,您可以跳过这些障碍,因为它已经保证不对彼此重新排序易失性访问。
如果您有C11支持,那么最好使用_Atomics,这还可以保证正常的赋值不会根据您的x重新排序,并且访问是原子的。
编辑: GCC (以及clang)在这方面似乎不一致,也不会总是这么做。我打开了GCC关于这件事的错误报告。
https://stackoverflow.com/questions/38741512
复制相似问题