gpt4 book ai didi

multithreading - 减少缓存行失效的总线流量

转载 作者:行者123 更新时间:2023-12-03 09:27:16 25 4
gpt4 key购买 nike

共享内存多处理系统通常需要为缓存一致性生成大量流量。核心 A 写入缓存。 Core B 稍后可能会读取相同的内存位置。因此,内核 A,即使它本来可以避免写入主内存,也需要向内核 B 发送通知,告诉 B 如果该地址正在缓存中,则该地址无效。
究竟什么时候需要这样做,这是一个复杂的问题。不同的 CPU 架构有不同的内存模型,这里上下文中的内存模型是一组关于观察到的事情发生的顺序的保证。内存模型越弱,A 在发送通知的确切时间就越放松对于 B,A 和 B 更容易并行做更多的事情。不同 CPU 架构的内存模型总结:https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering
所有的讨论似乎都是关于失效发生的时间,事情发生的顺序。
但在我看来,在许多工作负载中,A 写入的大部分数据永远不会被 B 使用,因此如果可以完全消除那些缓存失效的总线流量会更好。专用于执行缓存一致性的硬件仍然需要存在,因为 A 和 B 有时需要共享数据,但是写入共享总线是 CPU 可以做的更耗能的事情之一,并且电池生命周期和散热通常是现在限制资源,因此减少总线流量将是一个有用的优化。有没有办法做到这一点?
从效率的角度来看,理想的情况是如果忽略总线流量是默认的(因为大多数写入的数据不与其他线程共享),并且您必须在需要缓存一致性的地方显式地发出内存屏障。另一方面,这可能是不可能的,因为假设它在 x86 或 ARM 上运行的现有代码量很大;有没有办法反过来,向 CPU 指示给定的缓存行永远不会对任何其他线程感兴趣?
我会对任何系统的答案感兴趣,但最特别是 x64、ARM 或 RISC-V 上 Linux 最常见的当前/ future 服务器配置。

最佳答案

真正的 CPU 不使用共享总线;流量通过一个 L3 缓存,它的标签用作监听过滤器(特别是在单插槽 Intel 芯片中)。或在其他微架构上节省流量的类似方法。您是对的,当您扩展到多个内核时,实际上向每个其他内核广播消息的功耗和性能成本会高得令人望而却步。 共享总线只是像 MESI 这样的协议(protocol)的简单思维模型,而不是现代 CPU 中的真正实现。 What cache coherence solution do modern x86 CPUs use?例如。
带有写分配的回写缓存需要在您存储到缓存行之前读取缓存行,因此它们具有该行其他部分的原始数据。这种读取,当由写入触发时,称为“所有权读取”(RFO),以使线路进入 MESI 独占状态(可以在没有外部流量的情况下转换为脏修改)。 RFO 包括失效。
如果初始访问是只读的,如果没有其他内核具有缓存副本 ,则该行通常会像 RFO 一样以独占状态到达(即它在 L3(最后一级)缓存中丢失)。这意味着对于读取一些私有(private)数据然后修改它的常见模式,流量保持在最低水平。
我认为,多 socket 系统必须监听另一个 socket 或咨询监听过滤器来确定这一点,但对功率/能量最敏感的系统是移动的(总是单 socket )。

有趣的事实:Skylake-X 之前的 Intel 2-socket Xeon 芯片(例如 E5 ...-v4)没有用于套接字之间流量的监听过滤器,并且只是在 QPI 链路上的另一个套接字上进行垃圾邮件监听。 E7 CPU(能够在四核和更大的系统中使用)具有专用的监听过滤器缓存来跟踪热线的状态,以及足够的 QPI 链接来交叉连接更多的插槽。来源:John McCalpin's post on an Intel forum ,虽然我还没有找到太多其他数据。也许约翰在考虑像 Core2/Nehalem Xeons 这样的早期系统,英特尔确实在其中谈到了具有监听过滤器的功能,例如
https://www.intel.ca/content/dam/doc/white-paper/quick-path-interconnect-introduction-paper.pdf将 QPI 与其早期设置进行比较。并且有一些关于可以权衡延迟与吞吐量的监听模式的更多细节。也许英特尔只是没有以同样的方式使用术语“窥探过滤器”。

Is there a way to do it the other way around, to indicate to the CPU that a given cache line will never be of interest to any other thread?


如果您有将存储数据与失效相结合的缓存写入协议(protocol),则可以跳过 RFO。 例如x86 具有绕过缓存的 NT 存储,并且显然快速字符串存储( rep stos/ rep movs )甚至在 ERMSB 也可以使用无 RFO 写入协议(protocol)( at least in P6, according to Andy Glew who designed it )之前,即使它们将数据留在缓存中等级制度。但是,这仍然需要使其他缓存失效,除非该内核已经拥有处于 E 或 M 状态的线路。 Enhanced REP MOVSB for memcpy
有些 CPU 确实有一些 scratchpad memory这是每个核心真正私有(private)的。 它根本不共享,因此不需要或不可能进行显式刷新。请参阅带宽博士在 Can you directly access the cache using assembly? 上的回答- 这在 DSP 上显然很常见。

但除此之外,通常不会,CPU 不提供将部分内存地址空间视为非一致性的方法。一致性是 CPU 不想让软件禁用的保证。 (也许是因为它可能会造成安全问题,例如,如果在操作系统对文件数据页面进行校验和之后,某些旧的写入最终会在文件数据页面中可见,但在 DMA 到磁盘之前,非特权用户空间可能会导致校验和 FS,如 BTRFS 或 ZFS查看文件中的坏块 mmap(PROT_WRITE|PROT_READ, MAP_SHARED)。)
通常内存屏障的工作原理是简单地让当前核心等待直到存储缓冲区排入 L1d 缓存(即先前的存储已成为全局可见),因此如果您允许非一致性 L1d,则需要一些其他机制来刷新它。 (例如,x86 clflushclwb 强制写回外部缓存。)
为大多数软件创建利用这一点的方法是很困难的。例如假设您可以获取本地 var 的地址并将其传递给其他线程。即使在单线程程序中,任何指针也可能来自 mmap(MAP_SHARED) .因此,您不能默认将堆栈空间映射为非连贯或类似的东西,并且编译程序以使用额外的刷新指令,以防它们获得指向确实需要可见的非连贯内存的指针,毕竟这将完全失败整个事情的目的。
所以这不值得追求的部分原因是,为了提高效率,堆栈上的所有东西都必须关心,这是额外的复杂性。 Snoop 过滤器和基于目录的一致性足以解决这个问题,总体而言比期望每个人都针对这个低级功能优化他们的代码要好得多!

关于multithreading - 减少缓存行失效的总线流量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62614838/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com