当从连续的内存位置执行一系列_mm_stream_load_si128()调用(MOVNTDQA)时,硬件预取器是否仍会启动,或者我是否应该使用显式软件预取(带有NTA提示)以获得预取的好处,同时仍然避免缓存污染?
我问这个的原因是因为他们的目标对我来说似乎是矛盾的。流式加载将绕过高速缓存获取数据,而预取器试图主动将数据提取到高速缓存中。
当顺序迭代大型数据结构(处理的数据不会在很长一段时间内被修饰)时,对我来说避免污染chache层次结构是有意义的,但我不想因为预取程序空闲而导致频繁的~100个周期的惩罚。
目标架构为英特尔SandyBridge
发布于 2015-08-20 20:36:13
根据Patrick Fay (Intel)'s Nov 2011 post:的说法,“在最新的英特尔处理器上,预取将一条线从内存引入L1数据缓存(而不是其他缓存级别)。”他还说,你需要确保你的预取不会太晚(HW prefetch已经把它拉到所有级别),或者太早(当你到达那里的时候已经被驱逐了)。
正如在关于OP的评论中所讨论的那样,当前的英特尔CPU有一个大的共享L3,其中包括所有的每核缓存。这意味着高速缓存一致性业务只需检查L3标签,以查看高速缓存线是否可能在每核L1/L2中的某处被修改。
IDK如何使Pat Fay的解释与我对高速缓存一致性/高速缓存层次结构的理解相一致。我想,如果它真的在L1中运行,它也必须在L3中运行。也许L1标记有某种标志来表明此行是弱有序的?我最好的猜测是他是在简化,当它实际上只在填充缓冲区中时才说L1。
此Intel guide about working with video RAM讨论使用加载/存储缓冲区而不是缓存线的非临时性移动。(请注意,这可能只适用于不可缓存的内存。)它没有提到预取。它也很古老,早于SandyBridge。然而,它确实有这样一句有趣的话:
普通加载指令以指令请求的相同大小为单位从USWC内存中提取数据。相比之下,诸如MOVNTDQA之类的流加载指令通常会将数据的整个缓存线拉到CPU中的特殊“填充缓冲区”。随后的流式加载将从该填充缓冲区读取,导致的延迟要小得多。
然后在另一段中,说典型的CPU有8到10个填充缓冲区。SnB/Haswell still have 10 per core.。同样,请注意,这可能只适用于不可缓存的内存区域。
WB (回写)内存上的movntdqa不是弱有序的(see the NT loads section of the linked answer),因此不允许它是“陈旧的”。与NT存储不同,movntdqa和prefetchnta都不会更改回写内存的内存排序语义。
我没有测试过这个猜测,但是在现代的Intel上,prefetchnta / movntdqa可以将高速缓存线加载到L3和L1中,但可以跳过L2 (因为L2不包含或排除L1)。NT提示可以通过将缓存线放在其组的LRU位置来产生效果,在那里它是下一条要被逐出的线。(正常缓存策略在MRU位置插入新行,最远被逐出。参见this article about IvB's adaptive L3 policy for more about cache insertion policy)。
IvyBridge上的预取吞吐量仅为每43个周期一个,所以如果您不希望预取降低IvB上的代码速度,请注意不要预取太多。来源:Agner Fog's insn tables and microarch guide。这是一个特定于IvB的性能错误。在其他设计中,太多的预取只会占用本可以是有用指令的uop吞吐量(除了预取无用地址的危害)。
关于一般的软件预取(不是nt类型):Linus Torvalds发布了他们如何预取( rarely help in the Linux kernel, and often do more harm than good )。显然,在链表末尾预取空指针会导致速度减慢,因为它会尝试TLB填充。
发布于 2015-08-25 02:05:13
这个问题让我做了一些阅读...看看英特尔的MOVNTDQA手册(使用9月14日的版本),有一个有趣的声明-
如果存储源是WC (写组合)存储器类型,则处理器实现可以利用与该指令相关联的非临时提示。如果存储器源是WB (回写)存储器类型,则实现还可以利用与该指令相关联的非临时提示。
后来-
如果为非临时读取指定的存储器地址不是WC存储器区域,则被读取区域的存储器类型可以覆盖非临时提示。
因此,除非你的mem类型是WC,否则似乎不能保证非时态提示会做任何事情。我真的不知道WB memtype注释是什么意思,也许有些Intel处理器确实允许你使用它来减少缓存污染,或者他们想在未来保留这个选项(所以你不会开始在WB mem上使用MOVNTDQA,并假设它的行为总是一样的),但很明显,WC mem是这里真正的用例。您希望此指令能够为原本完全无法缓存的内容提供一些短期缓冲。
现在,从另一方面看预取的描述*
从不可缓存或WC内存中的
预取将被忽略。
因此,这基本上结束了故事-你的想法是绝对正确的,这两个可能不是故意的,也不太可能在一起工作,其中一个可能会被忽略。
好的,但是这两个有可能真的工作吗(如果处理器为WB内存实现NT加载)?好吧,再次阅读MOVNTDQA,一些其他的东西吸引了我们的眼球:
将侦听并刷新缓存中任何内存类型的别名行。
唉哟。因此,如果你以某种方式设法预取到你的缓存中,你实际上很可能会降低任何连续流加载的性能,因为它必须首先刷新行。这可不是个好主意。
发布于 2017-12-24 16:45:35
我最近对不同的prefetch风格做了一些测试,而answering another question和我的发现是:
使用prefetchnta的结果与以下在Skylake客户端上的实现一致:
prefetchnta将值加载到L1和L3中,但不加载到L2中(实际上,如果该行已经存在,则该行似乎可以从L2中被逐出)。LRU _当前的优化手册(248966-038年)在一些地方声称,prefetchnta确实将数据带入L2,但只以一种方式脱离了集合。例如,在7.6.2.1视频编码器中
为视频编码器实现了预取缓存管理,减少了内存流量。通过防止一次性使用的视频帧数据进入二级缓存来保证二级缓存污染的减少。使用非临时PREFETCH (PREFETCHNTA)指令使数据仅进入二级高速缓存的一个路,从而减少二级高速缓存的污染。
这与我在Skylake上的测试结果不一致,在Skylake上,使用prefetchnta跨越64 KiB区域显示的性能与从L3获取数据几乎完全一致(每个负载大约4个周期,MLP因子为10,L3延迟约为40个周期):
Cycles ns
64-KiB parallel loads 1.00 0.39
64-KiB parallel prefetcht0 2.00 0.77
64-KiB parallel prefetcht1 1.21 0.47
64-KiB parallel prefetcht2 1.30 0.50
64-KiB parallel prefetchnta 3.96 1.53由于Skylake中的L2是4路的,如果数据被加载到一个路中,那么它应该只会勉强留在L2缓存中(其中一个路覆盖64 KiB),但上面的结果表明它不是这样。
您可以使用我的uarch-bench程序在您自己的Linux硬件上运行这些测试。旧系统的结果将特别有趣。
Skylake服务器(SKLX)
在具有different L3缓存架构的Skylake服务器上,prefetchnta的报告行为与Skylake客户端有很大不同。特别是,使用prefetchnta获取的用户神秘reports that行在任何缓存级中都不可用,一旦从L1中逐出,就必须从reports that中重新读取。
最可能的解释是,由于prefetchnta,它们根本没有进入L3 -这很可能是因为在Skylake服务器中,L3是私有L2缓存的非包含性共享牺牲品缓存,因此使用prefetchnta绕过L2缓存的行可能永远没有机会进入L3。这使得prefetchnta在功能上更加纯粹:被prefetchnta请求污染的缓存级别更少,但也更脆弱:在被驱逐之前从L1读取nta行的任何失败都意味着对内存的另一次完整往返:由prefetchnta触发的初始请求完全浪费。
https://stackoverflow.com/questions/32103968
复制相似问题