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 应该未被使用,除非未来调用或推送指令。
存储转发到 lock
ed 操作的负载端可能是一回事(如果最近使用了 space),因此保持锁定操作狭窄是好的。但作为一个完整的障碍,我不希望有任何存储从它的输出转发到其他任何东西,所以与正常情况不同,窄(1 字节)lock orb
没有那个缺点。
mfence
与堆栈的热线 space 相比相当糟糕,甚至在 Haswell 上,在 Skylake 上可能更糟,它甚至会阻止 OoO exec。 (与 lock add
相比,AMD 也很糟糕)。
走虚位联动路线时,有几点需要考虑:
- 在这个核心的L1d,
- 未被其他内核使用
- 不创建长依赖链
- 避免因存储转发未命中而停顿
没有上下文,任何事情都只是猜测,所以我们的目标是做出最好的猜测。
靠近堆栈顶部的位置是 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();
虚拟联锁比_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 应该未被使用,除非未来调用或推送指令。
存储转发到 lock
ed 操作的负载端可能是一回事(如果最近使用了 space),因此保持锁定操作狭窄是好的。但作为一个完整的障碍,我不希望有任何存储从它的输出转发到其他任何东西,所以与正常情况不同,窄(1 字节)lock orb
没有那个缺点。
mfence
与堆栈的热线 space 相比相当糟糕,甚至在 Haswell 上,在 Skylake 上可能更糟,它甚至会阻止 OoO exec。 (与 lock add
相比,AMD 也很糟糕)。
走虚位联动路线时,有几点需要考虑:
- 在这个核心的L1d,
- 未被其他内核使用
- 不创建长依赖链
- 避免因存储转发未命中而停顿
没有上下文,任何事情都只是猜测,所以我们的目标是做出最好的猜测。
靠近堆栈顶部的位置是 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存在(
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();