单个进程中的两个不同线程可以通过读取和/或写入共享一个公共(public)内存位置。
通常,这种(有意的)共享是通过使用 lock
的原子操作来实现的。 x86 上的前缀,对于 lock
前缀本身(即非竞争成本)以及缓存行实际共享时的额外一致性成本(true 或 false 共享)。
在这里,我对单线程 P
的生产消费者成本感兴趣。写入内存位置,另一个线程`C从内存位置读取,都使用普通读取和写入。
当在同一插槽上的不同内核上执行此类操作时,以及在最近的 x86 内核上在同一物理内核上的兄弟超线程上执行时,此类操作的延迟和吞吐量是多少。
在标题中,我使用术语“超 sibling ”来指代在同一内核的两个逻辑线程上运行的两个线程,而内核间 sibling 是指在不同物理内核上运行的两个线程的更常见情况.
最佳答案
杀手问题是内核进行推测性读取,这意味着每次写入推测性读取地址(或更正确地写入同一高速缓存行)在“完成”之前意味着 CPU 必须撤消读取(至少如果您是 x86),这实际上意味着它会取消该指令及以后的所有推测指令。
在读取退出之前的某个时刻,它会“完成”,即。以前的任何指令都不会失败,也没有任何理由重新发出,CPU 可以像以前执行过所有指令一样行事。
其他核心示例
除了取消指令之外,它们还在播放缓存乒乓球,所以这应该比 HT 版本更糟。
让我们从流程中的某个点开始,其中包含共享数据的缓存行刚刚被标记为共享,因为消费者要求读取它。
因此,消费者可以在它获得共享缓存行之间的时间段内前进,直到它再次失效。不清楚可以同时完成多少次读取,很可能是 2 次,因为 CPU 有 2 个读取端口。只要 CPU 的内部状态得到满足,它就不需要重新运行它们,它们不能在每个之间都不会失败。
同核HT
这里两个HT共享核心,必须共享资源。
缓存行应该始终保持独占状态,因为它们共享缓存,因此不需要缓存协议(protocol)。
现在为什么在 HT 核心上需要这么多周期?让我们从刚刚读取共享值的消费者开始。
因此,对于每次读取共享值,消费者都会重置。
结论
不同的核心显然每次在每个缓存乒乓之间进步如此之多,以至于它的性能比 HT 更好。
如果 CPU 等着看这个值是否真的改变了,会发生什么?
对于测试代码,HT 版本的运行速度会快得多,甚至可能与私有(private)写入版本一样快。由于缓存未命中覆盖了重新发布延迟,因此不同的核心不会运行得更快。
但是如果数据不同,就会出现同样的问题,除了不同的核心版本会更糟,因为它还必须等待缓存行,然后重新发布。
因此,如果 OP 可以更改一些角色,让时间戳生产者从共享中读取并承受性能损失,那就更好了。
阅读更多 here
关于performance - 生产者-消费者在超同级与非超同级之间共享内存位置的延迟和吞吐量成本是多少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45602699/