加载数字流时避免缓存污染

Raj*_*jiv 7 optimization x86 assembly x86-64 cpu-cache

在x86处理器上有没有办法将数据从常规写回内存加载到寄存器而不通过缓存层次结构?

我的用例是我有一个很大的查找结构(Hash map或B-Tree).我正在处理大量的数字(比我的L3大得多但适合内存).我想做的很简单:

 int result = 0;
 for (num : stream_numbers) {
     int lookup_result = lookup_using_b_tree(num);
     result += do_some_math_that_touches_registers_only(lookup_result);
 }
 return result;
Run Code Online (Sandbox Code Playgroud)

由于我只访问每个数字一次并且所有数字的总和超过L3大小,我想他们最终会驱逐一些缓存行来保存我的B树的一部分.相反,我理想的是不喜欢这个流命中缓存中的任何数字,因为它们根本没有时间局部性(只读一次).这样我可以最大化我的B树保留在缓存中的机会,并且查找速度更快.

我已经查看了(v)movntdqaSSE 4.1中有关时间负载的说明.这似乎不太合适,因为它似乎只适用于组合内存的不可缓存的写入.英特尔的这篇旧文章声称:

未来几代英特尔处理器可能包含流加载的优化和增强功能,例如增加流加载缓冲区的利用率和支持额外的内存类型,为软件开发人员提供更多机会,以提高其应用程序的性能和能效.

但是我今天没有意识到任何这样的处理器.我已经在其他地方读过,处理器可以选择忽略这个回写内存的提示并使用movdqa替代.那么有什么方法可以实现从常规回写内存加载而不通过x86处理器上的缓存层次结构,即使它只能在Haswell和后来的模型上实现?我还要感谢有关未来是否可能的任何信息?

Joh*_*ica 0

是的,您可以将MOVNTI值直接存储到内存中,而无需它们接触缓存。

MOVNTI 的延迟约为 400 个周期(在 Skylake 上)。
但是,如果您只是存储值,则您不太关心延迟,而更关心倒数吞吐量,即每个 1 个周期MOVNTI

请注意,完成存储后,您需要执行 SFENCE 或 MFENCE。

根据我的实验MOVNTI(在 ZeroMem 例程的背景下),如果您编写的内容超过 512 KB,那么付出的努力是值得的。
确切的值主要取决于缓存大小等。

非临时性仅适用于写入,不适用于读取!
事实上,我不知道任何 NT-mov 变体在读取数据时以非时间方式工作。

但是,如果您正在进行读取-修改-写入循环,那么使用非临时移动就没有意义。
您还需要考虑节点结构的局部性。
它可能看起来像这样:

left, right: pointer_to_node  (8 bytes aligned on 32 byte boundary).
data: integer;                (4 bytes) 
....
Run Code Online (Sandbox Code Playgroud)

如果是这样,您读取left/right节点指针会将其data内的内容吸入 32 字节 (*) 缓存行。
仅对数据执行 NT-mov 在这里没有帮助,在读取其他节点数据时它已经被吸入,因此已经在缓存中。

编译器在缓存友好边界上对齐数据结构这一事实确保了每次节点指针访问时都能将最大数量的节点数据放入缓存中。

(*) 高速缓存行大小取决于处理器。