非临时负载和硬件预取器,它们一起工作吗?

时间:2015-08-19 19:23:24

标签: performance x86 sse cpu-cache prefetch

从连续的内存位置执行一系列_mm_stream_load_si128()调用(MOVNTDQA)时,硬件预取器是否仍会启动,或者我应该使用显式软件预取(带有NTA提示)为了获得预取的好处,同时仍然避免缓存污染?

我之所以这样说是因为他们的目标似乎与我相矛盾。流加载将获取绕过缓存的数据,而预取器尝试主动将数据提取到缓存中。

当顺序迭代大型数据结构时(处理后的数据不会长时间润饰),对我来说避免污染chache层次结构是有意义的,但我不想经常发生~100次循环处罚因为预取者是空闲的。

目标架构是Intel SandyBridge

4 个答案:

答案 0 :(得分:9)

根据Patrick Fay (Intel)'s Nov 2011 post:,"在最近的英特尔处理器上,prefetchnta将一行从内存带入L1数据缓存(而不是其他缓存级别)。"他还说你需要确保你没有预取太晚(硬件预取已经把它拉到了所有级别),或者说太早了(当你到达那里时被驱逐)。

正如关于OP的评论中所讨论的,当前的Intel CPU具有大的共享L3,其包括所有每核心缓存。这意味着缓存一致性流量只需检查L3标记,以查看是否可以在每个核心L1 / L2中的某处修改缓存行。

IDK如何根据我对缓存一致性/缓存层次结构的理解来调和Pat Fay的解释。我想如果它确实进入L1,它也必须进入L3。也许L1标签有某种标志,说这条线是弱排序的?我最好的猜测是他正在简化,当它实际上只进入填充缓冲区时说L1。

这个Intel guide about working with video RAM讨论了使用加载/存储缓冲区而不是缓存行的非时间移动。 (请注意,这可能只适用于不可缓存的内存。)它没有提到预取。它还老了,比SandyBridge早。但是,确实有这个多汁的引用:

  

普通加载指令以US为单位从USWC内存中提取数据   指令请求的大小相同。相比之下,流媒体负载   诸如MOVNTDQA之类的指令通常会拉出一个完整的缓存行   数据到特殊的#34;填充缓冲区"在CPU中。后续流加载   将从该填充缓冲区中读取,从而减少延迟。

然后在另一段中,典型的CPU有8到10个填充缓冲区。 SnB/Haswell still have 10 per core.。再次注意,这可能仅适用于不可缓存的内存区域。

WB(回写)内存上的

movntdqa 弱排序(see the NT loads section of the linked answer),因此不允许"陈旧&#34 34 ;.与NT存储不同,movntdqaprefetchnta都不会改变回写存储器的内存排序语义。

我没有测试过这个猜测,但现代英特尔CPU上的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吞吐量,这可能是有用的指令(除了预取无用地址的危害)。

关于一般的SW预取(不是nt类型):Linus Torvalds发布了他们rarely help in the Linux kernel, and often do more harm than good的方式。显然在链表的末尾预取NULL指针会导致速度减慢,因为它会尝试填充TLB。

答案 1 :(得分:6)

这个问题让我做了一些阅读......看一下MOVNTDQA的英特尔手册(使用Sep' 14版),这是一个有趣的陈述 -

  

处理器实现可以使用非时间提示   如果内存源是WC(写入,则与此指令相关联)   结合)记忆类型。实现也可以使用   如果存储器,则与该指令相关联的非时间提示   source是WB(写回)内存类型。

以及稍后 -

  

正在读取的区域的存储器类型可以覆盖非时间区域   提示,如果为非时间读取指定的内存地址不是   WC内存区域。

因此,除非你的mem类型是WC,否则似乎无法保证非临时提示会做任何事情。我真的不知道WB memtype评论意味着什么,也许一些英特尔处理器确实允许你使用它来减少缓存污染的好处,或者他们希望将来保留这个选项(所以你不要这样做) ; t开始在WB mem上使用MOVNTDQA并假设它总是表现相同),但很明显WC mem是真正的用例。您希望此指令为那些本来完全无法缓存的内容提供一些短期缓冲。

现在,另一方面,查看prefetch *的描述:

  

忽略来自不可缓存或WC内存的预取。

所以这几乎关闭了故事 - 你的想法是完全正确的,这两个可能没有意义,也不可能一起工作,很可能会忽略其中一个。

好的,但这两个实际上是否有效(如果处理器为WB内存实现NT加载)?那么,从MOVNTDQA再次阅读,其他东西引人注目:

  

将缓存缓存中的任何内存类型别名行   冲洗。

哎哟。因此,如果您以某种方式设法预取到缓存中,您实际上可能会降低任何连续流加载的性能,因为它必须首先刷新线路。不是很好想。

答案 2 :(得分:4)

我最近在answering another question期间对各种prefetch口味进行了一些测试,我的发现是:

使用prefetchnta的结果与Skylake客户端上的以下实现一致:

  • prefetchnta将值加载到L1L3中,但不加载L2(实际上,如果L2,行似乎可以从prefetchnta中删除已经存在了。)
  • 似乎将值“正常”加载到L1中,但是在L3中以较弱的方式将其驱逐得更快(例如,仅在集合中以单一方式,或者将其LRU标志设置为使得它将是下一个受害者)。
  • 与所有其他预取指令一样,
  • prefetchnta使用LFB条目,因此它们并不能真正帮助您获得额外的并行性:但NTA提示在此可用于避免L2和L3污染。

当前的优化手册(248966-038)在一些地方声称prefetchnta确实将数据带入L2,但只是以一种方式离开集合。例如,在 7.6.2.1视频编码器

  

为视频编码器实现的预取高速缓存管理   减少内存流量。二级缓存污染减少   通过防止一次性视频帧数据进入   二级缓存。使用非时间PREFETCH(PREFETCHNTA)   指令只将数据带入二级缓存的一种方式,   从而减少了二级缓存的污染。

这与我对Skylake的测试结果不一致,其中使用 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 跨越64 KiB区域显示的性能与从L3获取数据几乎完全一致(每个负载约4个周期,具有MLP因子) 10和3.3潜伏期约40个周期):

prefetchnta

由于Skylake中的L2是4路,如果数据以单向方式加载,它应该只停留在L2缓存中(其中一种方式覆盖64 KiB),但上面的结果表明它没有

您可以使用我的uarch-bench程序在Linux上的自己的硬件上运行这些测试。旧系统的结果将特别有趣。

Skylake Server(SKLX)

Skylake Server上prefetchnta报告的行为具有different L3缓存架构,与Skylake客户端有很大不同。特别是,使用prefetchnta获取的用户Mysticial reports that行在任何缓存级别都不可用,并且一旦从L1中逐出,就必须从DRAM中重新读取。

最有可能的解释是,由于prefetchnta,它们根本没有进入L3 - 这可能是因为在Skylake服务器中L3是私有L2缓存的非包容性共享牺牲缓存,所以使用prefetchnta绕过L2缓存的行可能永远不会有机会进入L3。这使得prefetchnta在功能上更加纯净:nta请求污染更少的缓存级别,但也更脆弱:任何在被驱逐之前从L1读取prefetchnta行的失败意味着另一个完全往返记忆:{{1}}触发的初始请求完全被浪费了。

答案 3 :(得分:2)

MOVNTDQA(在WC内存上)和PREFETCHNTA都不会影响或触发任何缓存硬件预取程序。非时间提示的整个想法是完全避免缓存污染或至少尽可能地减少它。

只有极少数(未记录)的缓冲区称为流加载缓冲区(这些缓冲区与行填充缓冲区和L1缓存区分开),用于保存使用MOVNTDQA获取的缓存行。所以基本上你需要立即使用你获取的内容。此外,MOVNTDQA仅适用于WC内存。

PREFETCHNTA指令非常适合您的场景,但您必须弄清楚如何在代码中正确使用它。来自英特尔优化手册第7.1节:

  

如果您的算法是单通道,请使用PREFETCHNTA。如果您的算法是   多次使用PREFETCHT0。

PREFETCHNTA指令具有以下优点:

  • 它将包含指定地址的特定缓存行至少提取到L3缓存和/或可能更高级别的缓存层次结构中(参见Bee和Peter的回答以及第7.3.2节) 。在它被缓存的每个缓存级别中,如果需要从集合中逐出一行,它可能/应该/更可能被认为是第一个被驱逐的。在使用PREFETCHNTA增强的单遍算法(例如计算大数字的平均值)的实现中,稍后预取的高速缓存行可以放置在与预取的那些行相同的块中。使用PREFETCHNTA。因此,即使提取的数据总量很大,整个缓存中只有一种方式会受到影响。以其他方式驻留的数据将保持缓存状态,并在算法终止后可用。但这是一把双刃剑。如果两个PREFETCHNTA指令彼此太靠近,并且指定的地址映射到同一个缓存集,那么只有一个会存活。
  • 使用PREFETCHNTA预取的缓存行与使用相同硬件一致性机制的任何其他缓存行一样保持连贯。
  • 适用于WB,WC和WT内存类型。很可能你的数据存储在WB内存中。
  • 就像我之前说过的那样,它不会触发硬件预取。正是出于这个原因,它也可以用于改善英特尔推荐的不规则存储器访问模式的性能。

执行PREFETCHNTA的线程可能无法从中有效地受益,具体取决于同一物理内核上,同一处理器的其他物理内核上或其他内核上的任何其他正在运行的线程的行为共享相同一致性域的处理器。诸如固定,优先级提升,基于CAT的缓存分区和禁用超线程等技术可以帮助该线程高效运行。另请注意,PREFETCHNTA被归类为推测性加载,因此它与三个fence指令同时发生。