lock addl $0, (%esp)是mfence的替代品,而不是lfence。
例如假设对齐std::atomic a,b:
movl $1, a a = 1; Atomic for aligned a
# barrier needed here
movl b, %eax tmp = b; Atomic for aligned b
你的选择是:
使用xchg执行顺序一致性存储,例如: mov $1, %eax / xchg %eax, a所以你不需要一个单独的屏障;它是商店的一部分。我认为这是大多数现代硬件上最有效的选择;除了gcc之外的C ++ 11编译器将xchg用于seq_cst存储。
使用mfence作为障碍。 (gcc使用mov + mfence作为seq_cst商店)。
使用lock addl $0, (%esp)作为障碍。任何locked指令都是完全障碍。 Does lock xchg have the same behavior as mfence?
(或者到其他一些位置,但是堆栈在L1d中几乎总是私有且很热,所以它是一个很好的候选者。但是这可能会使用堆栈底部的数据创建一个依赖链。)
您只能将xchg折叠成商店,因为它无条件地使用不依赖于旧值的值写入内存位置。
如果可能的话,使用xchg作为seq-cst商店可能是最好的,即使它也从共享位置读取。 mfence在最近的英特尔CPU(Are loads and stores the only instructions that gets reordered?)上比预期慢,同样阻止了lfence的无序执行非独立非内存指令。
甚至当lock addl $0, (%esp)/(%rsp)可用时,甚至可能值得使用mfence而不是mfence,但我没有尝试过这些缺点。使用-64(%rsp)或某些东西可能会降低对热点(本地或返回地址)的数据依赖性的可能性,但这可能使像valgrind这样的工具不高兴。
lfence对于存储器排序永远不会有用,除非您使用MOVNTDQA负载读取视频RAM(或其他一些WC弱序列区域)。
序列化乱序执行(但不是存储缓冲区)对于停止StoreLoad重新排序(x86的强内存模型允许普通WB(回写)内存区域的唯一类型)没有用。
lfence的真实用例是用于阻止rdtsc的无序执行,以便对非常短的代码块进行计时,或者通过阻止通过条件或间接分支的推测来阻止Spectre。
另请参阅When should I use _mm_sfence _mm_lfence and _mm_mfence(我的答案和@ BeeOnRope的答案),了解更多关于lfence无效的原因,以及何时使用每个屏障指令。 (或者在我的C ++内部函数中用C ++而不是asm编程)。