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

Blu*_*rat 9 performance x86 sse prefetch cpu-cache

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

我问这个的原因是因为他们的目标似乎与我相矛盾.流加载将获取绕过缓存的数据,而预取器尝试主动将数据提取到缓存中.

当顺序迭代一个大型数据结构(处理过的数据不会在很长一段时间内被修饰)时,我有必要避免污染chache层次结构,但我不想因频繁出现频繁的~100次循环处罚-fetcher闲置.

目标架构是Intel SandyBridge

Pet*_*des 9

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


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

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

这篇关于使用视频RAM的英特尔指南讨论了使用加载/存储缓冲区而非缓存线的非时间移动.(请注意,这可能仅适用于不可缓存的内存.)它没有提到预取.它也很老,早于SandyBridge.但它确实有这个多汁的报价:

普通加载指令以与指令请求相同大小的单位从USWC存储器中提取数据.相比之下,诸如MOVNTDQA的流加载指令通常将完整的高速缓存行数据拉到CPU中的特殊"填充缓冲区".随后的流加载将从该填充缓冲区读取,从而导致更少的延迟.

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

movntdqa在WB(回写)内存没有弱排序(请参阅链接答案的NT加载部分),因此不允许"陈旧".不像NT店,既不movntdqa也不prefetchnta改变内存排序写回内存的语义.

我还没有测试这个猜测,但prefetchnta/ movntdqa现代Intel的CPU可以加载的高速缓存线到L3和L1上,但可以跳过L2(L2因为不包括或不包括L1的).NT提示可以通过将缓存行放置在其集合的LRU位置来实现,其中它是下一行要驱逐的行.(正常缓存策略在MRU位置插入新行,离被驱逐最远.有关缓存插入策略的更多信息,请参阅有关IvB自适应L3策略的文章).


IvyBridge上的预取吞吐量仅为每43个周期一个,因此如果您不希望预取会降低IvB上的代码速度,请注意不要预取太多.来源:Agner Fog的insn表和微观指南.这是IvB特有的性能错误.在其他设计中,过多的预取只会占用uop吞吐量,这可能是有用的指令(除了预取无用地址的危害).

关于一般的SW预取(不是nt那种):Linus Torvalds发布了他们很少在Linux内核中提供帮助的方法,而且往往弊大于利.显然在链表的末尾预取NULL指针会导致速度减慢,因为它会尝试TLB填充.

  • +1好的研究!是的,我完全禁用了针对Ivy Bridge的任何预取.我可以确认预取空值是一个糟糕的主意.这是我试图避免使用特定功能的"无预取"版本的方法.完全没用.VTune对我大吼大叫. (2认同)
  • @Leeor:IvB每43个循环只能退出一个`prefetch*'指令.SnB和Haswell可以每0.5个周期退休一次.(它们在加载端口上运行.)因此,使用预取过载它会导致预取指令本身成为IvB的瓶颈,尤其是.当数据已经在缓存中时. (2认同)
  • 当我在VTune下查看它时,案例1(单独的流加载)显示在这些加载中花费的所有时间.这里不足为奇,他们来自记忆.在情况2和3(使用预取)中,VTune显示所有时间都花在预取本身上,并且在流加载中花费的时间为零.这让我感到意外,因为它表明飞行中的预取数量有限,并且在达到限制时会阻止执行.如果它们没有阻塞,如果内存控制器无法跟上预取请求,则应该在负载中显示惩罚. (2认同)
  • @Mysticial:英特尔的手册暗示`prefetchNTA`进入L1D和(进入一种方式)L3,绕过L2.在SKX上,它也可以绕过L3,因为它不再具有包容性(并且只更新某种标签).也许SKX在L1D中也有污染限制,只能获取任何给定集合的一种方式?`32/8 = 4`,所以如果NT预取只使用单一的L1D方式,那么4kiB只是勉强大到可以踩到数据.(IDK,如果这可能是*设计更改,但尝试更小的预取距离).否则可能是某种设计错误...... (2认同)

Lee*_*eor 6

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

如果存储器源是WC(写入组合)存储器类型,则处理器实现可以利用与该指令相关联的非时间提示.如果存储器源是WB(写回)存储器类型,则实现还可以利用与该指令相关联的非时间提示.

后来 -

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

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

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

来自不可缓存或WC内存的预取将被忽略.

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

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

缓存中的任何内存类型别名行都将被窥探和刷新.

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


Bee*_*ope 6

我最近prefetch回答另一个问题时对各种口味进行了一些测试,我的发现是:

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

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

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

为视频编码器实施的预取缓存管理减少了内存流量。防止一次性视频帧数据进入二级缓存,保证二级缓存污染减少。使用非临时性PREFETCH(PREFETCHNTA)指令将数据只带入二级缓存的一个通道,从而减少二级缓存的污染。

这与我在 Skylake 上的测试结果不一致,其中跨越 64 KiB 区域,prefetchnta显示的性能几乎与从 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
Run Code Online (Sandbox Code Playgroud)

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

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

天湖服务器 (SKLX)

报告的prefetchntaSkylake Server 的行为与 Skylake 客户端明显不同,它具有不同的L3 缓存架构。特别是,用户 Mysticial报告说,使用提取的行在prefetchnta任何缓存级别中都不可用,一旦从 L1 中逐出,就必须从 DRAM 中重新读取。

最可能的解释是他们根本没有进入 L3,因为prefetchnta- 这很可能是因为在 Skylake 服务器中,L3 是私有 L2 缓存的非包含共享受害者缓存,因此绕过 L2 缓存使用的行prefetchnta是可能永远没有机会进入L3。这使得prefetchnta两者在功能上更加纯粹:更少的缓存级别被prefetchnta请求污染,但也更脆弱:nta在被驱逐之前从 L1读取一行的任何失败都意味着另一个完整的内存往返:由 触发的初始请求prefetchnta完全浪费了。


归档时间:

查看次数:

2512 次

最近记录:

7 年,5 月 前