微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

写无效策略如何与组关联缓存一起使用?

如何解决写无效策略如何与组关联缓存一起使用?

我正在阅读 norman P. Jouppi 的 Cache Write Policies 论文,我理解为什么 write-invalidate(定义在第 193 页)与直接映射缓存配合良好,这是因为能够写入检查数据的数据标记,如果发现未命中,则缓存行将因写入损坏而无效。这可以在一个周期内完成。 但是如果 write-invalidate 用于设置关联的缓存有什么好处吗? 实际处理器中用于 L1 缓存的常用配置是什么?他们使用直接或设置关联和写入验证/写入/写入无效/写入时获取策略吗?

解决方法

TL:DR:对于使用写无效的非阻塞缓存,将其从直接映射更改为集关联可能会损害命中率,除非写入是非常罕见,或者意味着您引入了需要阻止的可能性。

Write-invalidate 仅对带有简单缓存的简单有序管道有意义,即使没有存储缓冲区,它也会尝试避免管道停顿,并且以牺牲命中率为代价非常快。如果你打算改变一些东西来提高命中率,那么从写无效(通常是写回 + 写分配 + 写入时获取)改变将是第一件事。使用集合关联缓存写入无效是可能的,但要进行一些丑陋的权衡,但您不会喜欢结果。


您链接的 1993 年论文使用该术语表示 the modern cache-coherence mechanism meaning 以外的其他内容。在论文中:

组合 命中前写、写时不取指和不写分配 我们称之为写无效

是的,现在现实世界的缓存基本上都是集合关联的;对于相同的数据大小,更复杂的标签比较器逻辑值得增加的命中率。 Which cache mapping technique is used in intel core i7 processor? 有一些通用的东西,不仅仅是 x86。直接映射缓存的现代示例包括 DRAM 缓存,当英特尔平台上的一部分持久内存在内存模式下运行时。此外,来自多个供应商的许多服务器级处理器都支持 L3 方式分区,因此您可以,例如,为线程分配一种方式,该方式基本上类似于直接映射缓存。

对于现代 CPU 缓存,写入策略通常是写入分配 + 写入时取回 + 命中前不写入;一些 ISA 提供了一些方法,例如特殊指令来绕过不会很快重新读取的“非临时”存储的缓存,以避免这些情况下的缓存污染。大多数工作负载确实以足够的时间局部性重新加载他们的存储,写分配是唯一明智的选择,特别是当缓存更大和/或关联性更强时,它们更有可能挂在一行上直到下一次读取或写。

对同一行进行多次小写入也很常见,这使得写入分配非常很有价值,尤其是在存储缓冲区无法合并这些写入的情况下。


但是如果将 write-invalidate 用于 set-associative 缓存有什么好处吗?

好像不是。

它唯一的优点是不会拖延一个简单的有序管道,它没有存储缓冲区(论文中的“写缓冲区”)。它允许与标记检查并行写入,因此您可以在修改该行之后 发现是否命中。 (现代 CPUdo 使用存储缓冲区来decouple store commit to L1d from store execution 并隐藏存储未命中延迟。即使是有序 CPU 通常也有一个存储缓冲区,以允许 RFO 的内存级并行(读取-所有权)。(例如手机中的 ARM Cortex-A53)。

无论如何,在集合关联缓存中,您需要检查标签以了解在写入命中时写入集合的哪个“方式”。(或检测未命中并选择一个根据某些策略驱逐,例如使用一些额外状态位的随机或伪 LRU,或者如果没有写分配则写回)。 如果你等到标签检查之后才找到写入方式,你就失去了写入无效的唯一好处

盲目地以随机方式写入可能会导致命中方式与您猜测的方式不同。方式预测是一回事(并且可以比随机做得更好),但是像这样的写入错误预测的缺点是不必要地使一行无效,而不仅仅是一点额外的延迟。 Way prediction in modern cache。我不知道通常会实现什么样的成功率方式预测。我猜不是很好,最多可能是 80% 到 90%。可能将晶体管用于方式预测会更好地用于其他地方,做一些比写无效更糟糕的事情!带有存储转发的存储缓冲区可能成本更高,但要好得多。

write-invalidate 的优点是帮助缓存非阻塞。但是,如果您确实以您选择的方式以外的方式发现写命中时需要纠正这种情况,则需要返回并纠正这种情况,更新正确的行。所以你会失去非阻塞属性。 从不停顿比通常停顿要好,因为这意味着您根本不需要让硬件处理这种可能的情况。 (尽管您确实需要能够为内存停顿。)

通过以所有方式写入,可以避免单向写入命中另一种情况。但是最多只有一次命中,其余的将不得不失效。对命中率的负面影响将随着关联性显着增加。 (除非写入相对于读取非常罕见,否则降低关联性可能会帮助写入全向策略的命中率,因此对于给定的总缓存容量,直接映射可能是最佳选择如果您坚持完全非阻塞写入无效。)即使对于直接映射缓存,论文本身给出的实验评估表明,与其他评估的写入策略相比,写入无效具有更高的未命中率。因此,只有当减少延迟和带宽需求的好处超过高未命中率的损害时,它才能获胜。

此外,正如我所说,写分配对 CPU 非常有用,尤其是当它是集合关联时,因此您要花费更多资源来尝试获得更高的命中率。您也许仍然可以通过在未命中时触发提取来实现写分配,记住您在行中存储数据的位置,并在它到达时将其与该行的旧副本合并。

你不想通过吹掉不需要死的线来打败它。

此外,write-invalidate 意味着即使对于写命中也进行直写,因为如果一行脏了,它可能会丢失数据。 但是回写在现代 L1d 缓存中也非常好,可以将较大/较慢的缓存与写入带宽隔离。 (特别是如果没有每个核心的私有 L2 来单独减少共享缓存的总流量。)但是,AMD Bulldozer 系列确实有一个直写 L1d,在它和回写 L2 之间有一个小的 4k 写缓冲区。这通常被认为是一个失败的实验或设计的弱点,他们放弃了它,转而支持 Zen 的标准回写写分配 L1d。 When use write-through cache policy for pages

所以总而言之,write-invalidate 与现代主流 CPU 设计已确定为最佳选项的几项不兼容,您会在大多数主流 CPU 设计中找到这些选项

  • 写入分配写入策略
  • 回写(不是直写)。 https://en.wikipedia.org/wiki/Cache_(computing)#Writing_policies
  • set-associative(只能通过方式预测部分缓解的巨大缺点)
  • 存储缓冲区将存储未命中与执行分离,并允许内存并行。 (并非严格不兼容,但存储缓冲区使其毫无意义。对于 OoO exec 是必需的,并广泛用于 in-order)

在缓存一致的 SMP 系统中写入无效

您永远不会考虑在单芯片多核 CPU 中使用它;在开始构建更多内核之前,在每个内核上花费更多的晶体管以获得更多唾手可得的成果。例如适当的存储缓冲区。如果您希望多个低 IPC 线程的高吞吐量停顿很多,请使用某种 SMT。

但是对于多路 SMP,如果您想使用多个您可以构建的最大单核芯片,这在历史上可能是有意义的,而这仍然不够大,只能有一个存储缓冲区而不是这个。

我想在私有中型回写集关联 L2 前面使用一个非常“瘦”的直接映射直写 L1d 甚至可能是有意义的,但仍然相当快 . (也许将其称为 L0d 缓存,因为它可以一个无序存储缓冲区。下一级缓存仍然会看到大量来自低命中的读取和写入-这个小的直接映射缓存的速率。)

通常所有缓存(包括 L1d)都是同一个全局一致性域的一部分,因此在您拥有独占所有权之前无法写入 L1d 缓存。 (作为标签检查的一部分,你会检查它。)但如果这个 L1d / L0d 不是那样,那么它就不是连贯的,更像是一个存储缓冲区。

当然,您需要将 L2 的直写排队,并在无法跟上时最终停止,因此您只是增加了复杂性。直写到 L2 机制还需要处理在写入之前等待 L2 获得该行的独占所有权(MESI 独占或修改状态)。所以这在很大程度上只是一个无序的存储缓冲区。

写入一条尚未到达 L2 的行的情况很有趣:如果它是 L0d 写入命中,您可以有效地免费获得存储合并。为此,您需要按字或按字节需要写回位(又名脏位)。通常,直写会在行内偏移量仍然可用时沿写入发送,但如果 L2 尚未准备好接受它(例如,由于写入未命中),则您不能这样做。这将它变成了一个写组合缓冲区。将整行标记为需要回写不起作用,因为未写入的部分仍然无效。

但是如果在尚未完成回写到 L2 的行上发生写入未命中(相同的缓存行,不同的标记位),那么您将遇到一个大问题,因为您将使仍然“脏”的行无效"(有一些旧商店数据的唯一副本)。在写入之前您无法检测到;重点是与检查标签并行编写。

仍有可能使这项工作继续进行:如果缓存访问是一种读+写交换,将先前的值保存在一个字缓冲区中(或任何最大写入大小),您仍然拥有所有数据.停止所有内容(包括此行的回写,以便您不会在一致的 L2 缓存中全局可见错误数据)。然后交换回来,等待该 L0d 行的旧状态实际写回该地址,然后将 tmp 缓冲区存储到 L0d 并更新标记和需要回写位以反映该存储。因此,附近商店之间的混叠变得更加昂贵并且会导致管道停滞。或者,您可以让非内存指令继续执行,只在下一个加载或存储时停止执行。 (如果您有足够的晶体管预算来避免大部分停顿,您可能可以使用完全不同的策略,例如拥有一个存储缓冲区和一个普通的 L1d。)

为了可用(假设您解决了dirty-store-miss问题),您需要某种方法来跟踪存储(和加载)的相对顺序。如果这就像确保整个 L0d 中的每个条目在允许另一个写入之前完成其直写过程一样简单,那么即使是存储-存储屏障也将非常昂贵。 CPU 执行的订单跟踪越少,屏障的成本就越高(确保刷新更多的东西)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。