在x86上实现std::atomic_thread_fence(std::memory_order_seq_cst),而不会产生额外的性能惩罚。

问题描述 投票:1回答:1

请提出一个后续问题 为什么这个 "std::atomic_thread_fence "能用?

作为一个假人联锁操作比 _mm_mfence,而且有不少实现方式,应该使用哪种联锁操作,在什么数据上使用?

假设使用一个内联汇编,它不知道周围的上下文,但可以告诉编译器它clobbers了哪些寄存器。

c++ x86 inline-assembly micro-optimization memory-barriers
1个回答
1
投票

暂时简答,不说太详细的原因。 具体请看 论语 在那个链接的问题上。

lock orb $0, -1(%rsp) 可能是一个不错的选择,以避免延长被spilledreloaded的本地变量的依赖链。参见 https:/shipilev.netblog2014on-the-fence-with-dependencies(依赖性) 为基准。 在Windows x64上(无红区),除了未来的呼叫或推送指令外,该空间应未被使用。

存储转发 负载方 locked操作可能是一件事(如果那个空间最近被使用了),所以保持锁定操作的狭窄是好的。 但是作为一个完整的屏障,我不期望可以有任何存储转发从它的输出到其他任何东西,所以与正常的不同,一个狭窄的(1字节)的 lock orb 并没有这个缺点。

mfence 是很垃圾的,即使在Haswell上也是堆栈空间的热线,在Skylake上可能更糟糕,它甚至阻挡了OOO exec.(在AMD上也很糟糕,相比于 lock add).


1
投票

在走假人位置联锁操作的路线时,需要考虑的问题不多。

  1. 在这个核心的L1d,
  2. 未被其他核心使用
  3. 不创建长的依赖链
  4. 避免因存储转发失误而导致停滞。

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

靠近栈顶的地方是1和2的好猜测。

刻意分配的堆栈变量很可能解决3,由于没有其他存储在飞行中,4不是问题。最好的操作是这样的 lock not.

不分配堆栈变量要求操作实际上是no-op,所以 lock or [mem], 0 是一个不错的选择。操作数应该是字节,以避免4的问题,对于3,总是猜测。(虽然可以使用返回地址,但没有上下文的汇编不知道。但是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会存在(硬件锁Elision是否因Spectre Mitigation而永远消失了?). 根据 RTM概述,一个空的RTM事务仍然是一个栅栏,所以它可以被使用。

一个成功提交的RTM区域由一个XBEGIN和一个XEND组成,即使RTM区域内没有任何内存操作,其排序语义也与LOCK前缀指令相同。

小心失败的事务或不支持的RTM。伪代码似乎如下。

if (rtm_supported && _xbegin() == 0xFFFFFFFF)
  _xend();
else
  dummy_interlocked_op();
© www.soinside.com 2019 - 2024. All rights reserved.