【问题标题】:If I don't use fences, how long could it take a core to see another core's writes?如果我不使用栅栏,一个核心需要多长时间才能看到另一个核心的写入?
【发布时间】:2018-12-19 22:05:12
【问题描述】:

我一直在尝试用 Google 搜索我的问题,但老实说,我不知道如何简洁地陈述问题。

假设我在多核 Intel 系统中有两个线程。这些线程在同一个 NUMA 节点上运行。假设线程 1 向 X 写入一次,然后只是偶尔向前读取。进一步假设,除其他外,线程 2 连续读取 X。如果我不使用内存栅栏,线程 1 写入 X 和线程 2 看到更新的值之间可能需要多长时间?

我知道 X 的写入将进入存储缓冲区并从那里进入缓存,此时 MESIF 将启动,线程 2 将通过 QPI 看到更新的值。 (或者至少这是我收集到的)。我假设存储缓冲区会在存储栅栏上被写入缓存,或者如果该存储缓冲区条目需要被重用,但我不知道存储缓冲区被分配给写入。

最终,我要为自己回答的问题是,线程 2 是否有可能在一个正在执行其他工作的相当复杂的应用程序中几秒钟内看不到线程 1 的写入。

【问题讨论】:

  • 如果两个线程在同一个NUMA节点上运行,则不会涉及QPI。

标签: x86 intel cpu-architecture memory-barriers lockless


【解决方案1】:

内存屏障不会使其他线程任何更快地看到您的存储。(除非阻塞稍后的加载可能会略微减少对提交缓冲存储的争用。)

存储缓冲区始终尝试尽快将已停用(已知的非推测性)存储提交到 L1d 缓存。缓存是连贯的1,因此由于 MESI/MESIF/MOESI,它们使它们全局可见。 store buffer 没有设计为适当的缓存或写组合缓冲区(尽管它可以将背靠背存储组合到同一缓存行),因此它需要清空自身以为新存储腾出空间。与缓存不同,它希望自己保持为空,而不是满。

注意 1:不仅仅是 x86;我们可以在其内核上运行单个 Linux 实例的任何 ISA 的所有多核系统都必须是缓存一致的; Linux 依靠volatile 的手动原子来使数据可见。同样,C++ std::atomic 和 mo_relaxed 的加载/存储操作只是在所有普通 CPU 上进行简单的 asm 加载和存储,依靠硬件来实现内核之间的可见性,而不是手动刷新。 When to use volatile with multi threading? 解释。有一些集群,或混合微控制器 + DSP ARM 板具有非相干共享内存,但我们不会跨不同的相干域运行同一进程的线程。相反,您在每个集群节点上运行一个单独的操作系统实例。我不知道atomic<T> 加载/存储包含手动刷新指令的任何 C++ 实现。 (如果有请告诉我。)


栅栏/屏障通过让当前线程等待来工作

...直到通过正常机制发生所需的可见性。

完整屏障的简单实现(mfence 或locked 操作)是在存储缓冲区耗尽之前停止管道,但高性能实现可以做得更好,并允许单独乱序执行来自内存顺序限制。

(不幸的是 Skylake's mfence does fully block out-of-order execution,修复了涉及从 WC 内存加载 NT 的模糊 SKL079 错误。但 lock add 或 xchg 或任何只会阻止以后加载读取 L1d 或存储缓冲区,直到屏障到达末尾存储缓冲区。而早期 CPU 上的 mfence 大概也没有这个问题。)


一般在非 x86 架构上(对于较弱的内存屏障有显式的 asm 指令,例如 only StoreStore fences 而不关心负载),原理是相同的:阻塞它需要阻塞的任何操作,直到该内核更早完成任何类型的操作。

相关:


最终我要为自己回答的问题是线程 2 是否有可能在几秒钟内看不到线程 1 的写入

不,最坏情况的延迟可能是存储缓冲区长度 (56 entries on Skylake, up from 42 in BDW) 乘以缓存未命中延迟,因为 x86 的强内存模型(无 StoreStore 重新排序)要求存储按顺序提交。但是多个缓存行的 RFO 可以同时运行,因此最大延迟可能是其 1/5(保守估计:有 10 个行填充缓冲区)。也可能存在来自飞行中的负载(或来自其他内核)的争用,但我们只需要一个数量级的粗略数。

假设 RFO 延迟(DRAM 或来自另一个内核)在 3GHz CPU 上是 300 个时钟周期(基本上是由组成的)。因此,商店变得全球可见的最坏情况延迟可能类似于300 * 56 / 5 = 3360 个核心时钟周期。所以在一个数量级内,最坏的情况是在我们假设的 3GHz CPU 上大约 1 微秒。 (CPU 频率抵消了,因此以纳秒为单位估算 RFO 延迟会更有用)。

此时所有您的商店需要等待很长时间才能收到 RFO,因为它们所有都位于未缓存或由其他内核拥有的位置。并且它们都不是背靠背到同一缓存行,因此没有一个可以合并到存储缓冲区中。所以通常你会期望它明显更快。

我认为没有任何合理的机制可以让它花费一百微秒,更不用说一整秒了。

如果您的所有存储都缓存其他内核都在竞争访问同一行的行,那么您的 RFO 可能需要比正常时间更长的时间,因此可能需要数十微秒,甚至可能是一百微秒。但这种绝对最坏的情况不会偶然发生。

【讨论】:

  • 我认为我们可以对存储到达另一个线程所需的时间进行建模,如下所示:存储退出的时间(这意味着将存储缓冲区留给 L1D 或LFB) + 将行从内核的私有缓存复制到另一个内核的私有缓存(或目标寄存器)所需的时间。这可能需要针对包含 L2 和不同物理内核的 L2-L2 传输。但是这两个时间分量都可以有很大的不同。很难设定上限。
  • @HadiBrais:从无序核心 (ROB) 中退出与到达存储缓冲区的末尾并提交到 L1d(到 M 状态的行)是分开的。存储缓冲区将 OoO 执行/退休与 L1d 提交分离。退休是提交的先决条件,但仅此而已。 (一个大的存储缓冲区会影响中断延迟,因为没有办法丢弃/回滚它;为了正确性,已经离开 ROB need 的存储。)我在计算中省略了 OoO exec,这对于计时 WRT 可能很重要。以低 IPC 代码加载。
  • 但是是的,事情可能会有很大的不同,这就是为什么我可以自信地排除一微秒,但我只是声称我对最坏情况延迟的 1 我们快速估计的数量级。
  • 考虑一个具有多个套接字的共享内存系统,其中两个线程位于两个不同的套接字上。一个线程获取另一个线程的数据需要多少时间?如果所有的核心都在忙着做事,最坏的情况会超过一秒钟吗?
  • @BeeOnRope:我链接到另一个问题的答案。该答案的最后一部分包含有关 SKL079 的更多详细信息以及我的结论。如果有人可以在 HSW 上测试 mfence 以查看它是否会阻止 OoO exec,那就太酷了。比较 HSW 数字的好主意;也许他们试图让mfence 在 SKL 中更高效,但最终恢复了它。我一直认为早期的 uarch 效率更高,但也许不是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
相关资源
最近更新 更多