【问题标题】:Does software prefetching allocate a Line Fill Buffer (LFB)?软件预取是否分配行填充缓冲区 (LFB)?
【发布时间】:2013-10-28 14:49:51
【问题描述】:

我意识到Little's Law 限制了在给定延迟和给定并发级别下传输数据的速度。如果您想更快地传输某些东西,您要么需要更大的传输,要么需要更多的“飞行中”传输,要么需要更低的延迟。对于从 RAM 读取的情况,并发性受 Line Fill Buffer 数量的限制。

当加载未命中 L1 缓存时,将分配行填充缓冲区。现代英特尔芯片(Nehalem、Sandy Bridge、Ivy Bridge、Haswell)每个核心有 10 个 LFB,因此每个核心限制为 10 个未完成的缓存未命中。如果 RAM 延迟为 70 ns(似是而非),并且每次传输为 128 字节(64B 缓存线加上其硬件预取双胞胎),则这将每个内核的带宽限制为:10 * 128B / 75 ns = ~16 GB/s。诸如单线程Stream 之类的基准确认这是相当准确的。

减少延迟的明显方法是使用 x64 指令(例如 PREFETCHT0、PREFETCHT1、PREFETCHT2 或 PREFETCHNTA)预取所需数据,这样就不必从 RAM 中读取数据。但是我无法通过使用它们来加快速度。问题似乎是 __mm_prefetch() 指令本身会消耗 LFB,因此它们也受到相同的限制。硬件预取不会触及 LFB,但也不会跨越页面边界。

但我在任何地方都找不到任何记录。我发现的最接近的是 15 岁的 article,上面提到 Pentium III 上的预取使用 Line Fill Buffers。我担心从那以后事情可能会发生变化。而且由于我认为 LFB 与 L1 缓存相关联,我不确定为什么预取到 L2 或 L3 会消耗它们。然而,我测量的速度与这种情况是一致的。

那么:有没有什么方法可以在不使用这 10 个行填充缓冲区之一的情况下从内存中的新位置启动提取,从而通过绕过利特尔定律来实现更高的带宽?

【问题讨论】:

  • 非常有趣的阅读。您是否设法进一步解决了这个问题?一些观察。我在 Sandy Bridge 笔记本电脑上达到每核 16GB/s(峰值 21GB/s)。此外,我还测试了 Nehalem E7-4870,每个插槽的峰值带宽为 34GB/s。在单核上我只能达到 5GB/s,尽管两种架构都有 10 个填充缓冲区。使用您的模型,这表明延迟是 200ns 的 3 倍,我认为这是不现实的。两种架构的延迟应该相当。
  • @angainor:多核 Xeon 在非核中确实有更高的延迟,因此与笔记本电脑/台式机芯片相比,单核 L3/内存带宽要低得多(请参阅“延迟绑定平台”部分of this answer 了解详细信息+链接)。多插槽也很痛苦:它必须在从本地 DRAM 加载之前窥探另一个插槽的缓存。 Xeons 的优势在于来自多个线程的聚合内存带宽。
  • 我认为 Skylake 已将 LFB 的数量从 10 个增加到 12 个。

标签: caching assembly 64-bit bandwidth prefetch


【解决方案1】:

根据我的测试,所有类型的预取指令都会消耗最新 Intel 主流 CPU 上的行填充缓冲区

特别是I added some load & prefetch tests to uarch-bench,它在各种大小的缓冲区上使用大步幅加载。以下是我的 Skylake i7-6700HQ 上的典型结果:

                     Benchmark   Cycles    Nanos
  16-KiB parallel        loads     0.50     0.19
  16-KiB parallel   prefetcht0     0.50     0.19
  16-KiB parallel   prefetcht1     1.15     0.44
  16-KiB parallel   prefetcht2     1.24     0.48
  16-KiB parallel prefetchtnta     0.50     0.19

  32-KiB parallel        loads     0.50     0.19
  32-KiB parallel   prefetcht0     0.50     0.19
  32-KiB parallel   prefetcht1     1.28     0.49
  32-KiB parallel   prefetcht2     1.28     0.49
  32-KiB parallel prefetchtnta     0.50     0.19

 128-KiB parallel        loads     1.00     0.39
 128-KiB parallel   prefetcht0     2.00     0.77
 128-KiB parallel   prefetcht1     1.31     0.50
 128-KiB parallel   prefetcht2     1.31     0.50
 128-KiB parallel prefetchtnta     4.10     1.58

 256-KiB parallel        loads     1.00     0.39
 256-KiB parallel   prefetcht0     2.00     0.77
 256-KiB parallel   prefetcht1     1.31     0.50
 256-KiB parallel   prefetcht2     1.31     0.50
 256-KiB parallel prefetchtnta     4.10     1.58

 512-KiB parallel        loads     4.09     1.58
 512-KiB parallel   prefetcht0     4.12     1.59
 512-KiB parallel   prefetcht1     3.80     1.46
 512-KiB parallel   prefetcht2     3.80     1.46
 512-KiB parallel prefetchtnta     4.10     1.58

2048-KiB parallel        loads     4.09     1.58
2048-KiB parallel   prefetcht0     4.12     1.59
2048-KiB parallel   prefetcht1     3.80     1.46
2048-KiB parallel   prefetcht2     3.80     1.46
2048-KiB parallel prefetchtnta    16.54     6.38

要注意的关键是,没有任何一种预取技术比任何缓冲区大小的加载都快得多。如果任何预取指令不使用 LFB,我们希望它对于适合它预取的缓存级别的基准测试来说非常快。例如prefetcht1 将行带入 L2,因此对于 128-KiB 测试,如果它不使用 LFB,我们可能会期望它比加载变体更快。

更确切地说,我们可以检查l1d_pend_miss.fb_full 计数器,其描述为:

请求需要 FB(填充缓冲区)条目但存在的次数 没有可用的条目。一个请求包括 加载、存储或软件预取的可缓存/不可缓存需求 说明

描述已经表明 SW 预取需要 LFB 条目,并且测试证实了这一点:对于所有类型的预取,对于任何以并发为限制因素的测试,这个数字都非常高。例如,对于 512-KiB prefetcht1 测试:

 Performance counter stats for './uarch-bench --test-name 512-KiB parallel   prefetcht1':

        38,345,242      branches                                                    
     1,074,657,384      cycles                                                      
       284,646,019      mem_inst_retired.all_loads                                   
     1,677,347,358      l1d_pend_miss.fb_full                  

fb_full 值大于周期数,这意味着 LFB 几乎一直都是满的(它可能大于周期数,因为每个周期最多两个负载可能需要一个 LFB)。这个工作负载是纯粹的预取,所以除了预取之外没有什么可以填充 LFB。

此测试的结果也符合 Leeor 引用的手册部分中声称的行为:

在某些情况下,PREFETCH 不会执行数据预取。 其中包括:

  • ...
  • 如果内存子系统用完请求缓冲区 一级缓存和二级缓存之间。

显然这里不是这种情况:当 LFB 填满时,预取请求不会被丢弃,而是像正常加载一样停止,直到资源可用(这不是不合理的行为:如果您要求软件预取,你可能想得到它,即使这意味着拖延)。

我们还注意到以下有趣的行为:

  • prefetcht1prefetcht2 之间似乎有一些小的差异,因为它们报告了 16-KiB 测试的不同性能(差异有所不同,但始终不同),但如果您重复测试,您将看到这更有可能只是运行间的变化,因为这些特定值有些不稳定(大多数其他值都非常稳定)。
  • 对于 L2 包含的测试,我们每个周期可以承受 1 个负载,但只能承受一个 prefetcht0 预取。这有点奇怪,因为prefetcht0 应该与负载非常相似(在 L1 情况下,它每个周期可以发出 2 个)。
  • 即使 L2 有约 12 个周期延迟,我们也能够完全隐藏延迟 LFB,仅使用 10 个 LFB:我们获得每个负载 1.0 个周期(受 L2 吞吐量限制),而不是 12 / 10 == 1.2 周期每个负载,我们' d 期望(最佳情况)LFB 是否是限制事实(fb_full 的非常低的值证实了这一点)。这可能是因为 12 个周期的延迟是一直到执行核心的全部加载到使用延迟,其中还包括几个周期的额外延迟(例如,L1 延迟是 4-5 个周期),所以实际花费在LFB 小于 10 个周期。
  • 对于 L3 测试,我们看到 3.8-4.1 个周期的值,非常接近基于 L3 加载到使用延迟的预期 42/10 = 4.2 个周期。因此,当我们达到 L3 时,我们肯定会受到 10 个 LFB 的限制。这里prefetcht1prefetcht2 始终比负载或prefetcht0 快0.3 个周期。给定 10 个 LFB,这等于减少了 3 个周期的占用,这或多或少可以解释为预取在 L2 处停止,而不是一直到 L1。
  • prefetchtnta 通常比 L1 以外的其他吞吐量低得多。这可能意味着prefetchtnta 实际上正在做它应该做的事情,并且似乎将线条带入 L1,而不是 L2,并且只是“弱”进入 L3。因此,对于包含 L2 的测试,它具有并发限制的吞吐量,就好像它正在命中 L3 缓存一样,而对于 2048-KiB 的情况(L3 缓存大小的 1/3),它具有命中主内存的性能。 prefetchnta limits L3 cache pollution (to something like only one way per set),所以我们似乎被驱逐了。

会不会不一样?

这是我在测试之前写的一个较旧的答案,推测它是如何工作的:

一般来说,我希望任何导致数据在 L1 中结束的预取 都会消耗行填充缓冲区,因为我相信 L1 和内存层次结构的其余部分之间的唯一路径是LFB1。因此,针对 L1 的 SW 和 HW 预取可能使用 LFB。

但是,这使得以 L2 或更高级别为目标的预取不消耗 LFB 的可能性仍然存在。对于硬件预取,我很确定是这种情况:您可以找到许多参考资料来解释硬件预取是一种机制,可以有效地获得超出 LFB 提供的最大 10 的内存并行度。此外,L2 预取器似乎无法根据需要使用 LFB:它​​们居住在 L2 中/附近并向更高级别发出请求,可能使用超级队列并且不需要 LFB。

剩下的是针对 L2(或更高版本)的软件预取,例如 prefetcht1prefetcht22。与 L2 生成的请求不同,这些请求从核心开始,因此它们需要某种方式从核心发出,这可能是通过 LFB。来自英特尔优化指南有以下有趣的引述(强调我的):

一般来说,软件预取到 L2 会显示出更多的好处 比 L1 预取。软件预取到 L1 将消耗关键 硬件资源(填充缓冲区),直到缓存线填充完成。 一个 软件预取到 L2 不持有这些资源,它是 不太可能对绩效产生负面影响。如果你确实使用 L1 软件预取,最好是软件预取服务 通过在 L2 缓存中的命中,所以硬件的时间长度 资源被最小化。

这似乎表明软件预取不消耗 LFB - 但此引用仅适用于 Knights Landing 架构,我无法为任何更主流的架构找到类似的语言。看来Knights Landing的缓存设计明显不同(或者引用错误)。


1 事实上,我认为即使是非临时存储也使用 LFB 来脱离执行核心——但它们的占用时间很短,因为它们一到达 L2可以进入超级队列(实际上不进入 L2)然后释放它们关联的 LFB。

2 我认为这两个都针对最近英特尔的 L2,但这也不清楚 - 也许t2 提示实际上针对某些 uarch 上的 LLC?

【讨论】:

  • 您认为 NT 商店在前往 LFB 的路上使用 L1D? Intel describes them 使用 LFB as 写入组合缓冲区,所以我认为这意味着存储直接进入 LFB。我认为负载执行单元直接连接到 LFB 以及 L1D 以支持在数据到达以满足需求负载时提前重启(也适用于来自 USWC 内存的movntdqa 负载),因此可能直接连接到存储单元,也是。
  • 如果你像你应该写的那样用背靠背的 NT 存储编写一个完整的缓存行,那么是的,如果 LFB 真的移交给超级队列而不是直接进入,那么 LFB 占用可能会很短从LFB到内存控制器。否则,LFB 将一直被占用,直到对 LFB 的其他需求刷新部分写入的 LFB。
  • @PeterCordes - 你指的是脚注1吗?这是一个错字——我本来打算写“使用 LFB”而不是“使用 L1”。话题还是很有趣的。我不知道如果,例如,你只用 NT 存储写入高速缓存行的一部分并且它最终被刷新:它是否必须等待读取剩余的(未写入的)字节,以便它可以发送完整的 64 -byte 行(因为 LFB 通常会移动整行)或者是否有一种特殊模式可以有效地对一行中的某些字节进行屏蔽 NT 写入?
  • 是的,我是。好问题。我忘记了我读过的关于这个的内容。据推测,内存系统有某种方式来表示不可缓存区域的非全行存储,因此它可能可以做到这一点,除非它只适用于 2 的幂大小。如果内存控制器将其转换为 RMW,或者它可以对 DDRx DRAM 进行部分突发存储,则 IDK。
  • 暂时忽略 NT 存储,我基本上将整个 L1 + LFB + L2 视为一个用于并发目的的单个单元(“私有缓存”)。它作为一个整体向线路发出外部请求并且必须响应来自其他核心的窥探。如果一个核心有一条处于 M 状态的线路,而一个窥探来自另一个核心,并且线路处于被从 L1 驱逐到 L2 的“中间”,会发生什么?一种选择是该行可能存在于 LFB 中,这可能意味着必须探测 LFB ...
【解决方案2】:

首先进行小修正 - 阅读optimization guide,您会注意到一些硬件预取器属于二级缓存,因此不受填充缓冲区数量的限制,而是受二级缓存对应项的限制。

“空间预取器”(您的意思是 colocated-64B 行,完成到 128B 块)就是其中之一,因此理论上,如果您获取每隔一行,您将能够获得更高的带宽(一些 DCU 预取器可能会尝试“为您填补空白”,但理论上它们应该具有较低的优先级,因此它可能会起作用)。

但是,“王”预取器是另一个家伙,“L2 流媒体”。第 2.1.5.4 节内容如下:

Streamer :此预取器监视来自 L1 缓存的读取请求,以获取地址的升序和降序序列。受监控的读取请求包括由加载和存储操作以及硬件预取器发起的 L1 DCache 请求,以及用于代码提取的 L1 ICache 请求。当检测到请求的前向或后向流时,会预取预期的缓存行。预取的缓存行必须在同一个 4K 页面中

重要的部分是-

流媒体可以在每次 L2 查找时发出两个预取请求。流光 最多可以在加载请求之前运行 20 行

这个 2:1 的比率意味着对于此预取器识别的访问流,它总是会在您的访问之前运行。确实,您不会在 L1 中自动看到这些行,但这确实意味着,如果一切正常,您应该始终为它们获得 L2 命中延迟(一旦预取流有足够的时间提前运行并减轻 L3/内存延迟)。您可能只有 10 个 LFB,但正如您在计算中指出的那样 - 访问延迟越短,您更换它们的速度越快,您可以达到的带宽就越高。这实质上是将L1 <-- mem 延迟分离为L1 <-- L2L2 <-- mem 的并行流。

至于标题中的问题 - 尝试填充 L1 的预取将需要一个行填充缓冲区来保存该级别的检索数据。这可能应该包括所有 L1 预取。至于 SW 预取,第 7.4.3 节说:

在某些情况下,PREFETCH 不会执行数据预取。其中包括:

  • PREFETCH 导致 DTLB(数据转换后备缓冲区)未命中。这适用于具有对应于系列 15、型号 0、1 或 2 的 CPUID 签名的 Pentium 4 处理器。PREFETCH 可解决 DTLB 未命中并在具有对应于系列 15、型号 3 的 CPUID 签名的 Pentium 4 处理器上获取数据。
  • 对导致故障/异常的指定地址的访问。
  • 如果内存子系统用完一级缓存和二级缓存之间的请求缓冲区。

...

所以我认为你是对的,软件预取不是人为增加未完成请求数量的方法。然而,同样的解释也适用于这里——如果您知道如何使用 SW 预取来提前足够好地访问您的线路,您可能能够减轻一些访问延迟并增加您的有效 BW。但是,这不适用于长流,原因有两个:1)您的缓存容量有限(即使预取是临时的,如 t0 风格),以及 2)您仍然需要支付全部 L1-->mem 延迟每次预取,所以你只是把你的压力向前一点——如果你的数据操作比内存访问快,你最终会赶上你的软件预取。因此,这只有在您可以提前足够好地预取所需的所有内容并保留在那里时才有效。

【讨论】:

  • 感谢您的详细信息!我同意 L2 和 L3 硬件预取 (HPF) 不使用 LFB——这就是我们从 8 GB/s 到 16 GB/s 的原因。我不知道 L1 HPF 是否使用 LFB。是的,英特尔似乎记录了 NTA 和 T0 软件预取确实通过 LFB 加载 L1。但是T1和T2呢?似乎他们不会,但基准不同意。也许并发性还有另一个限制因素?是的,目标是使用 SWP 来增加带宽 --- 但我还没有获得任何显着的好处。关键似乎是在不使用 LFB 的情况下暗示从 Mem 到 L3/L2 的负载。
  • 关于 L1 硬件预取 - 我在同一个文档中看到,发布它们的条件之一是 Not many other load misses are in progress。也许这表明他们也在相同的 LFB 上竞争,所以这被用作减压的手段。
猜你喜欢
  • 1970-01-01
  • 2013-09-26
  • 2011-02-20
  • 2013-02-21
  • 1970-01-01
  • 2021-07-21
  • 1970-01-01
  • 2018-02-07
  • 1970-01-01
相关资源
最近更新 更多