【问题标题】:How does cache associativity impact performance [duplicate]缓存关联性如何影响性能[重复]
【发布时间】:2019-12-12 03:57:00
【问题描述】:

我正在阅读 Andrey Akinshin 的“Pro .NET Benchmarking”,有一件事让我感到困惑(第 536 页)——解释 cache associativity 如何影响性能。在一次测试中,作者使用了ints 的 3 个正方形数组 1023x1023、1024x1024、1025x1025,并观察到在 1024x1024 的情况下访问第一列的速度较慢。

作者解释(背景资料,CPU 是 Intel,具有 32KB 内存的 L1 缓存,它是 8-way associative):

当N=1024时,这个差值正好是4096字节;它等于 临界步幅值。这意味着从第一个 列匹配相同的八个 L1 缓存行。我们真的没有 缓存带来的性能优势,因为我们不能使用它 高效:我们只有 512 字节(8 个缓存行 * 64 字节缓存 行大小)而不是原来的 32 KB。当我们迭代 循环中的第一列,相应的元素相互弹出 缓存。当 N=1023 和 N=1025 时,我们没有问题 关键步幅不再:所有元素都可以保存在缓存中,这 效率更高。

所以看起来惩罚来自以某种方式缩小缓存,只是因为主内存无法映射到完整缓存。

这让我觉得很奇怪,在阅读了 wiki 页面后,我会说性能损失来自解决地址冲突。由于每一行都可以潜在地映射到同一个缓存行,因此这是一个又一个的冲突,CPU 必须解决这些问题——这需要时间。

因此我的问题是,这里的性能问题的真正本质是什么。缓存的可访问内存大小较小,或者整个缓存可用但 CPU 花费更多时间来解决与映射的冲突。还是有其他原因?

【问题讨论】:

  • 所有访问的项目都映射到同一组缓存。该项目可以进入集合中 8 个缓存行中的任何一个,但由于在 64 个缓存行中仅使用 一个,因此只有 8 个缓存行可以保存数组中的数据(而不是 64* 8 = 512 个缓存行)。所以这就像缓存只有 512 个字节。它叫conflict miss。请记住:缓存不是完全关联的,相同的地址总是映射到相同的集合。

标签: performance caching cpu cpu-cache


【解决方案1】:

缓存是其他两个层之间的一层。在您的情况下,在 CPU 和 RAM 之间。在最好的情况下,CPU 很少需要等待从 RAM 中获取某些内容。在最坏的情况下,CPU 通常不得不等待。

1024 示例遇到了一个坏情况。对于从 RAM 请求的整列 所有 字词位于缓存中的 相同 单元格(或相同的 2 个单元格,如果使用 2 路关联缓存等)。

与此同时,CPU 并不关心——它从内存中向缓存请求一个字;缓存要么拥有它(快速访问),要么需要进入 RAM(慢速访问)才能获取它。 RAM 并不关心——它会响应请求,无论何时到来。

回到 1024。查看该数组在内存中的布局。该行的单元在 RAM 的连续字中;当一行完成时,下一行开始。稍加思考,您可以看到 中的连续单元格的地址相差 1024*N,当 N=4 或 8(或任何单元格大小)时。那是 2 的幂。

现在让我们看看缓存的相对简单的架构。 (这是“微不足道的”,因为它需要快速且易于实现。)它只需要从地址中取出几位来形成缓存“内存”中的地址。

由于 2 的幂,这些位将始终相同 - 因此访问相同的插槽。 (我省略了一些细节,比如现在需要很多位,因此需要缓存的大小、2 路等)

当它上面的进程(CPU)在该项目被需要空间的其他项目从缓存中撞出之前多次获取一个项目(单词)时,缓存很有用。

注意:这里指的是 CPU->RAM 缓存,而不是磁盘控制器缓存、数据库缓存、网站页面缓存等;他们使用更复杂的算法(通常是散列)而不是“从地址中挑选一些位”。

回到你的问题...

所以看起来惩罚来自以某种方式缩小缓存,只是因为主内存无法映射到完整缓存。

这句话存在概念上的问题。

  • 主内存未“映射到缓存”;查看虚拟地址和真实地址。
  • 如果缓存中没有所需的单词,就会受到惩罚。
  • “缩小缓存” -- 缓存大小固定,具体取决于所涉及的硬件。

定义:在这种情况下,“单词”是来自 RAM 的连续字节串。它总是(?) 2 字节的幂,并且位于实际地址空间中的某个倍数。缓存的“单词”取决于 CPU 的年份、缓存级别等。今天可能可以找到 4、8、16 字节的单词。同样,2 的幂和定位在多个...是简单的优化。

回到你的 1K*1K 数组,比如 4 字节数字。加起来是 4MB,加减(对于 1023、1025)。如果你有 8MB 的缓存,整个阵列最终会被加载,并且由于在缓存中,对阵列的进一步操作会更快。但是如果你有,比如说,1MB 的缓存,一些阵列会进入缓存,然后被撞出 - 反复。可能不会比没有缓存好多少。

【讨论】:

  • 谢谢您的回答,但是(不要误会我的意思),因为 L1 缓存是 32KB,您的回答并不能解释为什么特别是 1024x1024 比其他缓存慢的区别。毕竟这些数组中的任何一个都完全适合缓存。所以这里的关键点是缓存的关联性,因为它是速度差异的根源。
  • @greenoldman - 缓存中的“单词”有多宽? 16 个字节将包括来自 4 列的 INT。它是N路关联的吗?问题是否有可能来自 L2 缓存? (其他细节将有助于给出答案。)
  • 至于字的宽度我不知道。本例中的 L1 是 8 路关联的。可能会涉及L2,但我怀疑这是一个原因,幸运的是作者提供了1023、1024、1025尺寸的结果,所以速度较慢的部分正好在中间,它排除了太大或太小的猜测,等等。所以它确实是与 L1 的临界步幅对齐的“副作用”。
猜你喜欢
  • 1970-01-01
  • 2014-02-09
  • 2013-01-13
  • 2012-04-20
  • 2022-08-07
  • 2014-11-25
  • 2017-07-23
  • 1970-01-01
  • 2020-11-15
相关资源
最近更新 更多