真实的CPU不使用共享总线;流量通过L3缓存,其标签作为嗅探过滤器(特别是在单插槽英特尔芯片中)。或者其他微架构上类似的节省流量的方式。你说得对,向每个核心广播消息实际上会因为功耗和性能问题而变得极其昂贵,尤其是当你扩展到许多核心时。共享总线只是MESI等协议的一个简单的心理模型,而不是现代CPU的真实实现。例如,请参见
现代x86 CPU使用什么高速缓存一致性解决方案?。
写回缓存需要进行写分配,因此在存储之前需要读取缓存行,以便对于该行的其他部分具有原始数据。当写触发读取时,这个读取被称为“所有权读取”(RFO),以将该行转换为MESI独占状态(可以将其转换为脏修改而无需外部流量)。RFO包括失效。
如果初始访问是只读的,那么该行通常会像RFO一样以独占状态到达,如果没有其他核心缓存了副本(即在L3(最后一级)缓存中未命中)。这意味着对于读取某些私有数据然后修改它的常见模式,流量保持在最低限度。
我认为多插槽系统将不得不窥视其他插槽或查询窥视过滤器来确定此信息,但最注重功率/能量的系统是移动设备(始终是单插槽)。
有趣的事实:Skylake-X之前(例如E5 ...-v4),英特尔2插槽Xeon芯片在套接字之间的流量没有嗅探过滤器,只是通过QPI链接向另一个套接字发送嗅探。 E7 CPU(可用于四个或更大系统)具有专用的嗅探过滤器缓存,以跟踪热线的状态,以及足够的QPI链接来交叉连接更多套接字。来源:
John McCalpin在Intel论坛上的帖子,尽管我还没有找到其他数据。也许John想到了早期的系统,如Core2 / Nehalem Xeons,其中英特尔确实谈到了具有嗅探过滤器的问题,例如
https://www.intel.ca/content/dam/doc/white-paper/quick-path-interconnect-introduction-paper.pdf将QPI与早期设置进行比较。并提供了一些有关可以在延迟和吞吐量之间权衡的嗅探模式的更多细节。也许英特尔只是没有以相同的方式使用“嗅探过滤器”这个术语。
有没有办法反过来做,告诉CPU某个缓存行对于任何其他线程都不感兴趣?
如果您拥有将存储数据与使缓存无效结合的高速缓存写协议,则可以跳过RFOs。例如,x86具有绕过缓存的NT存储器,并且显然快速字符串存储器(rep stos
/ rep movs
)甚至在ERMSB之前也可以使用无RFO写协议(根据设计它的Andy Glew所说,在P6中至少如此),即使它们将其数据留在缓存层次结构中。但是,这仍然需要使其他缓存失效,除非该核心已经拥有E或M状态中的行。增强的memcpy REP MOVSB
一些CPU确实有一些仅属于每个核心的私有
scratchpad memory。它不被共享,因此不需要或不可能进行显式刷新。请参阅Dr. Bandwidth在
Can you directly access the cache using assembly?上的答案-这在DSP上很常见。
除此之外,通常情况下,CPU 不提供一种将内存地址空间的部分视为非协同的方法。协同性是 CPU 不想让软件禁用的保证。(可能是因为它可能会创建安全问题,例如,如果某些旧写入在操作系统对其进行校验和之后,在 DMA 到磁盘之前成为文件数据页中的可见内容,则非特权用户空间可以导致像 BTRFS 或 ZFS 这样的校验和文件系统查看文件中的坏块。)
通常,内存屏障的工作原理是使当前核心等待,直到存储缓冲区已经排空到 L1d 缓存(即先前的存储已经变成全局可见),因此,如果允许非协同 L1d,则需要其他机制来刷新它。(例如,x86 的 clflush 或 clwb 强制写回到外部缓存。)
创建大多数软件利用此功能的方式将很困难;例如,假定您可以获取本地变量的地址并将其传递给其他线程。即使在单线程程序中,任何指针也可能来自。因此,您不能默认将堆栈空间映射为非一致性或类似的东西,并编译程序以使用额外的刷新指令,以防它们获得指向非一致性内存的指针,这些指针确实需要在所有情况下都可见,否则会完全破坏整个事物的目的。
因此,这不值得追求的部分原因是,所有层级上的每个东西都必须关心此功能以使其高效。嗅探过滤器和基于目录的一致性是解决问题的足够方法,并且总体而言比期望每个人为此低级特性优化其代码要好得多!