有没有办法 "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。
假设我正在遍历 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 商店的正常用例是
查看评论中的讨论:这可能表现得更差。在执行此操作之前一定要对两种方式进行基准测试,最好是在包括多路系统在内的各种硬件上。
如果数组一开始就在缓存中很热,那几乎 肯定 更糟。我假设这是唯一可以触摸它的东西,而不是一系列修改中的 last。