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

时间:2016-06-18 08:45:59

标签: assembly optimization x86 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;

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

我已经查看了SSE 4.1中可用于临时负载的(v)movntdqa指令。这似乎不太合适,因为它似乎只适用于组合内存的不可缓存的写入。来自英特尔的旧article声称:

  

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

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

1 个答案:

答案 0 :(得分:0)

是的,您可以使用MOVNTI将值直接存储到内存中,而不会触及缓存。

MOVNTI的潜伏期约为400个周期(On Skylake) 但是,如果您只是存储值,那么您几乎不关心延迟,而更多关于倒数吞吐量,即每MOVNTI个1个周期。

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

根据我对MOVNTI的实验(在ZeroMem例程的上下文中),如果您的写入量超过512 KB,则值得付出努力。
确切的值将主要取决于缓存大小等。

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

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

left, right: pointer_to_node  (8 bytes aligned on 32 byte boundary).
data: integer;                (4 bytes) 
....

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

编译器在缓存友好边界上对齐数据结构这一事实确保了每个节点指针访问时最大量的节点数据都会被挂接到缓存中。

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