有没有办法 "unfetch" 缓存行?

Is there a way to "unfetch" a cache line?

假设我正在遍历 10 个不同的 4kb 整数数组,并递增它们:

int* buffers[10] = ...; // 10 4kb buffers, not next to each other
for (int i = 0; i < 10; i++) {
  for (int j = 0; j < 512; j++) {
    buffers[i][j]++;
  }
}

compiler/CPU 非常酷,可以为内部循环做一些缓存预取。棒极了。但是...

...我刚刚吃掉了多达 40kb 的缓存,并删除了我程序的其余部分在缓存中喜欢的数据。

如果我可以提示编译器或 CPU "I'm not touching this memory again in the foreseeable future, so you can reuse these cache lines":

那就太好了
int* buffers[10] = ...;
for (int i = 0; i < 10; i++) {
  for (int j = 0; j < 512; j++) {
    buffers[i][j]++;
  }
  // Unfetch entire 4kb buffer
  cpu_cache_unfetch(buffers[i], 4096);
}

cpu_cache_unfetch 在概念上会 "doom" 该范围内的任何缓存行,首先将它们丢弃。

最后,这意味着我的小代码片段使用 4kb 的缓存,而不是 40kb。它会重用 4kb 缓存 10 次。程序的其余部分将不胜感激。

这有意义吗?如果是这样,有没有办法做到这一点?

也很感激:让我知道我自己从根本上误解缓存的所有方式! =D

我只知道 x86 的答案。这绝对是特定于体系结构的;不同的 ISA 具有不同的缓存控制功能。


在 x86 上,是的,clflush / clflushopt,但它们每次执行只会逐出一个缓存行。 (他们 force 回写 + 逐出,就像您需要内存映射的非易失性存储一样)。我的理解是 clflushopt 对于这种情况通常不值得,而只是允许缓存污染发生。


理论上,将 NT 预取用于只读可能会加快速度,但这很脆弱(调整软件预取取决于硬件,弄错会造成很大伤害)。进行常规存储可能会取消 NT 预取的影响,并将该行保留在 L1、L2 和 L3 中最近使用的位置。


一种可能疯狂的方法是 NT 商店。加载整个缓存行数据(四个 16 字节向量 = 64 字节),然后使用 movntdq.

存储更新值

NT表示"non-temporal";在不久的将来(即使是另一个核心)不会再次引用数据时使用。 What is the meaning of "non temporal" memory accesses in x86 有一些非常通用的答案,但可能会有所帮助。


根据 Intel 手册,如果目标缓存行之前已缓存 (),NT 存储会逐出目标缓存行,因此它适用于您的用例。但是编译器必须确保在内部循环中达到 64 字节对齐边界,以便它可以读取一个或两个完整的缓存行,而不是读取一个的 32 字节和另一个的 32 字节,然后用 NT 逐出它在读取一行的最后 32 个字节之前存储。 (不过,指针数学在 asm 中很容易;编译器确实知道如何在对齐边界之前进行标量计算。)

NT 商店的正常用例是 ,但这个用例至少可能是一个胜利。


查看评论中的讨论:这可能表现得更差。在执行此操作之前一定要对两种方式进行基准测试,最好是在包括多路系统在内的各种硬件上。

如果数组一开始就在缓存中很热,那几乎 肯定 更糟。我假设这是唯一可以触摸它的东西,而不是一系列修改中的 last