在页面边界访问数据时速度变慢?

Slowdown when accessing data at page boundaries?

(我的问题是关于计算机体系结构和性能的理解。没有找到相关的论坛,所以post这里作为一般性问题。)

我有一个 C 程序,它访问虚拟地址 space 中相隔 X 字节的内存字。例如,for (int i=0;<some stop condition>;i+=X){array[i]=4;}.

我用 X 的变化值测量执行时间。有趣的是,当 X2 的幂并且与页面大小有关时,例如 X=1024,2048,4096,8192...,我的性能会大幅下降。但是在 X 的所有其他值上,如 10231025,没有减速。下图附上性能结果

我在几台个人机器上测试了我的程序,在 Intel CPU 上都是 运行 Linux 和 x86_64。

这种放缓的原因可能是什么?我们试过DRAM中的row buffer,L3 cache等,似乎没有意义...

更新(7 月 11 日)

我们在这里做了一点测试,在原来的代码中加入了 NOP 指令。而且放缓仍然存在。这有点否决了 4k 别名。冲突缓存未命中的原因更可能是这里的情况。

这里有两件事:

  • Set-associative 如果您只接触 multiple-of-4096 地址,缓存别名会产生冲突未命中。内部高速缓存(L1 和 L2)通常由物理地址中的一小部分位进行索引。所以跨越 4096 字节意味着这些地址位对于所有访问都是相同的,所以你只是 L1d 缓存中的一组,而 L2 中的一些小数字。

    跨越 1024 意味着您在 L1d 中仅使用 4 个集合,使用 2 的较小幂逐渐使用更多集合,但 non-power-of-2 分布在所有集合中。 (英特尔 CPUs 长期以来一直使用 32KiB 8 路关联 L1d 缓存;每路 32K/8 = 4K。Ice Lake 将其提升到 48K 12 路,因此集合仅依赖于的相同索引在页码下方的位上。对于想要与 TLB 并行索引的 VIPT 缓存来说,这不是巧合。)

    但是如果步幅为 non-power-of-2,您的访问将分布在缓存中的更多集合上。 Performance advantages of powers-of-2 sized data?(答案描述了这个 dis 优势)

    - 共享 L3 缓存可以抵抗来自大 power-of-2 偏移量的别名,因为它使用更复杂的索引功能。

  • 4k 别名(例如在某些 Intel CPUs 中)。尽管使用 only 存储这可能无关紧要。这主要是内存消歧的一个因素,当 CPU 必须快速确定负载是否可能正在重新加载 recently-stored 数据时,它在第一遍中通过仅查看 page-offset位。

    这可能不是您正在发生的事情,但有关更多详细信息,请参阅:
    L1 memory bandwidth: 50% drop in efficiency using addresses which differ by 4096+64 bytes
    Why are elementwise additions much faster in separate loops than in a combined loop?

这些影响之一或两者都可能是 Why is there huge performance hit in 2048x2048 versus 2047x2047 array multiplication?

的一个因素

另一个可能的因素是硬件预取在物理页面边界处停止。 Why does the speed of memcpy() drop dramatically every 4KB? 但是将步幅从 1024 更改为 1023 不会有很大帮助。 "Next-page"在IvyBridge及之后的预取只是TLB预取,不是下一页的数据。


对于这个答案的大部分,我都假设是 x86,但是缓存别名/conflict-miss 内容通常适用。 Set-associative 具有简单索引的缓存普遍用于 L1d 缓存。 (或者在较旧的 CPU 上,direct-mapped 每个“集合”只有 1 个成员)。 4k 混叠的东西可能主要是 Intel-specific.

跨虚拟页面边界预取也可能是一个普遍问题。