在英特尔手册第3卷中,有一个使用早期存储进行加载重新排序的示例。
最初为x = y = 0
核心1:
mov [x], 1
mov r2, [y]
核心2:
mov [y], 1
mov r1, [x]
所以r1 = r2 = 0
是可能的。问题是要求获取-释放是否禁止这种情况?在x86上存储是一个释放存储,所以我认为没有。例如:
核心1:
release(mov [x], 1)
mov r2, [y]
核心2:
mov [y], 1
acquire(mov r1, [x])
在这种情况下,如果acquire(mov r1, [x])
负载观察到0,则只能得出结论,根据C11标准内存模型规范,release(mov [x], 1)
与acquire(mov r1, [x])
不同步,并且它不提供任何可能禁止在内核2上重新排序mov [y], 1
和acquire(mov r1, [x])
的保证
1条答案
按热度按时间jutyujz01#
正确的获取/释放语义不能阻止StoreLoad重新排序,即在加载之前执行存储,并交换它们的顺序。这种重新排序在x86上的普通加载和存储指令中是允许的。
如果你想在C11中避免这样的重新排序,你需要在存储和加载上都使用
memory_order_seq_cst
。在x86汇编中,你需要在这两条指令之间使用一个屏障。mfence
可以达到这个目的,但是任何lock
ed的读-修改-写指令也可以。包括xchg
,即使没有lock
前缀,它也会这样做。因此,如果您查看为memory_order_seq_cst
操作生成的程序集,您将看到中间存在一些这样的障碍。(对于某些reasons,如lock add [rsp], 0
或xchg
(位于某些寄存器和内存之间,其内容并不重要),实际上可能比mfence
性能更高,因此有些编译器会这样做,尽管看起来很奇怪。)