std::atomic_thread_fence(std::memory_order_seq_cst) 在 x86 上的实现,没有额外的性能损失

An implementation of std::atomic_thread_fence(std::memory_order_seq_cst) on x86 without extra performance penalties

的后续问题

虚拟联锁比_mm_mfence好,实现的方法也很多,应该用哪种联锁,用在什么数据上?

假设使用不知道周围上下文的内联程序集,但可以告诉编译器它破坏了哪个注册。

暂时简短回答,不详细说明原因。具体参见 the discussion in comments 相关问题。

lock orb [=10=], -1(%rsp) 可能是避免延长获得 spilled/reloaded 的局部变量的依赖链的好选择。有关基准,请参阅 https://shipilev.net/blog/2014/on-the-fence-with-dependencies。在 Windows x64(无红色区域)上,space 应该未被使用,除非未来调用或推送指令。

存储转发 locked 操作的负载端可能是一回事(如果最近使用了 space),因此保持锁定操作狭窄是好的。但作为一个完整的障碍,我不希望有任何存储从它的输出转发到其他任何东西,所以与正常情况不同,窄(1 字节)lock orb 没有那个缺点。

mfence 与堆栈的热线 space 相比相当糟糕,甚至在 Haswell 上,在 Skylake 上可能更糟,它甚至会阻止 OoO exec。 (与 lock add 相比,AMD 也很糟糕)。

走虚位联动路线时,有几点需要考虑:

  1. 在这个核心的L1d,
  2. 未被其他内核使用
  3. 不创建长依赖链
  4. 避免因存储转发未命中而停顿

没有上下文,任何事情都只是猜测,所以我们的目标是做出最好的猜测。

靠近堆栈顶部的位置是 1 和 2 的一个很好的猜测。

故意分配的堆栈变量很可能会修复 3,并且由于没有其他商店在飞行中,所以 4 不是问题。最好的操作看起来像 lock not.

不分配堆栈变量要求操作实际上是空操作,因此 lock or [mem], 0 是一个不错的选择。操作数应该是 byte 以避免 4 出现问题。对于 3,它始终是一个猜测。 (虽然可以使用 return 地址,但没有上下文的程序集不知道它。但是 MSVC _AddressOfReturnAddress 可能是个好主意)

我读过红区。 Windows 上没有它可以进行额外的优化。

没有额外变量的

lock not byte ptr [esp-1] 在 Windows 上很好,因为数据被认为是易变的,不应使用。没有溢出的寄存器,所以没有错误的数据依赖。

具有 128 字节红色区域的 ABI 排除了 lock not byte ptr [esp-1] 的使用。堆栈之外的 128 个字节可能足以不是 L1d。尽管如此,由于红色区域不太可能用作通常的堆栈,@Peter Cordes 给出的答案看起来不错。

TSX 之所以受到质疑主要是因为它不存在(在给定 CPU 上不受支持,或者由于勘误修复或安全缓解措施而被禁用)。在可预见的将来,只有RTM存在(). According to RTM overview,一个空的RTM交易仍然是一个栅栏,所以可以使用。

A successfully committed RTM region consisting of an XBEGIN followed by an XEND, even with no memory operations in the RTM region, has the same ordering semantics as a LOCK prefixed instruction.

注意交易失败或不支持的 RTM。伪代码好像是这样的:

if (rtm_supported && _xbegin() == 0xFFFFFFFF)
  _xend();
else
  dummy_interlocked_op();