【问题标题】:In which condition DCU prefetcher start prefetching?DCU 预取器在什么条件下开始预取?
【发布时间】:2019-04-30 06:07:18
【问题描述】:

我正在阅读有关英特尔酷睿 i7 系统中可用的不同预取器的信息。 我进行了实验以了解何时调用这些预取器。

这是我的发现

  1. L1 IP 预取器在 3 次缓存未命中后开始预取。它只是 缓存命中预取。

  2. L2 相邻行预取器在第一次缓存未命中后开始预取 并在缓存未命中时预取。

  3. L2 H/W (stride) 预取器在第一次缓存未命中后开始预取 并在缓存命中时预取。

我无法理解 DCU 预取器的行为。它何时开始预取或调用?它是否在缓存命中或未命中时预取下一个缓存行?

我浏览过英特尔文档disclosure-of-hw-prefetcher,其中提到 - DCU 预取器将下一个缓存行提取到 L1-D 缓存中,但在开始预取时没有明确的信息。

谁能解释一下 DCU 预取器何时开始预取?

【问题讨论】:

  • 在询问英特尔在手册中所说的 DCU 预取器是什么?在任何 Intel 处理器中都没有 L1 邻线预取器之类的东西。
  • 是的,我说的是 DCU 预取器。
  • 根据此链接software.intel.com/en-us/articles/…,DCU Prefetcher 将下一个缓存行获取到 L1-D 缓存中。
  • 这有点令人困惑,因为“相邻行预取器”术语和“DCU 预取器”术语具有不同的具体含义。如果您指的是相邻行预取器,彼得的回答将是正确的。您可能应该编辑问题以使用 DCU 预取器术语。
  • 您能分享一下其他三个预取的结果和测试吗?

标签: x86 intel cpu-architecture cpu-cache prefetch


【解决方案1】:

AFAIK,英特尔 CPU 没有 L1 邻线预取器。

不过,它在 L2 中有一个,它试图完成一对 128 字节对齐的 64 字节高速缓存行。 (因此不一定是下一行,如果导致一行被缓存的需求缺失或其他预取是一对的高半部分,它可能是前一行。

另请参阅https://software.intel.com/en-us/forums/software-tuning-performance-optimization-platform-monitoring/topic/714832,以及 SO 上的许多“相关”链接,例如prefetching data at L1 and L2。不过,不确定其中任何一个是否比英特尔优化手册的预取部分有更多详细信息:https://software.intel.com/en-us/articles/intel-sdm#optimization

我不确定它是否有任何启发式方法来避免在只需要一对线中的一条时避免浪费带宽和缓存占用空间,而不是在有足够多的需求未命中时不预取。

【讨论】:

  • 我认为OP指的是DCU预取器,它是下一行预取器。否则,如果 OP 的意思是“相邻”一对连续缓存行中的另一个缓存行,那么你是对的。
  • 一共有四个数据预取器,OP在编号列表中提到了三个,所以我认为他们在问第四个。
【解决方案2】:

DCU 预取器不会以确定的方式预取行。它似乎具有与每个潜在预取请求相关的置信度值。如果置信度大于某个阈值,则仅触发预取。此外,如果两个 L1 预取器都启用,似乎只有其中一个可以在同一周期内发出预取请求。也许接受来自具有更高置信度的预取。下面的答案没有考虑这些观察结果。 (还有很多实验工作要做,以后会重写。)


英特尔手册告诉我们有关 DCU 预取器的一些信息。优化手册的第 2.4.5.4 节和第 2.5.4.2 节都这样说:

数据缓存单元 (DCU) 预取器 -- 此预取器,也称为 流式预取器,由对非常的上升访问触发 最近加载的数据。处理器假定此访问是一部分 流式算法并自动获取下一行。

请注意,第 2.4.5.4 节是 Sandy Bridge 部分的一部分,第 2.5.4.2 节是 Intel Core 部分的一部分。 DCU 预取器首先在英特尔酷睿微架构上得到支持,并且在所有后来的微架构上也得到支持。据我所知,没有迹象表明 DCU 预取器随着时间的推移发生了变化。所以我认为它至少在 Skylake 之前的所有微架构上都是一样的。

那句话并没有说太多。 “升序访问”部分表明预取器是由偏移量增加的多次访问触发的。 “最近加载的数据”部分含糊不清。它可以指在地址空间中要预取的行之前的一行或多行。也不清楚这是指虚拟地址还是物理地址。 “获取下一行”部分表明它每次触发时只获取一行,并且该行是触发预取的行之后的行。

我在 Haswell 上进行了一些实验,禁用了除 DCU 预取器之外的所有预取器。我还禁用了超线程。这使我能够孤立地研究 DCU 预取器。结果显示如下:

  • DCU 预取器最多可跟踪对 4 个不同的 4KB(可能是物理)页面的访问。
  • 当对同一缓存集中的一个或多个行进行三个或更多访问时,将触发 DCU 预取器。访问必须是按需加载或软件预取(任何预取指令,包括prefetchnta)或两者的组合。访问可以是 L1D 中的命中或未命中,也可以是两者的组合。当它被触发时,对于当前正在跟踪的 4 个页面,它将预取相应页面的 每个 内的下一行。例如,考虑以下三个需求负载未命中:0xF1000、0xF2008 和 0xF3004。假设被跟踪的 4 个页面是 0xF1000、0xF2000、0xF3000 和 0xF4000。然后 DCU 预取器将预取以下行:0xF1040、0xF2040、0xF3040 和 0xF4040。
  • 当对两个连续缓存集中的一个或多个行进行三个或更多访问时,将触发 DCU 预取器。就像以前一样,访问必须是按需加载或软件预取。访问可以是 L1D 中的命中或未命中。当它被触发时,对于当前正在跟踪的 4 个页面,它将预取相应页面的 每个 中的下一行,相对于具有较小物理地址的已访问缓存集。例如,考虑以下三个需求负载未命中:0xF1040、0xF2048 和 0xF3004。假设被跟踪的 4 个页面是 0xF1000、0xF2000、0xF3000 和 0xF4000。然后 DCU 预取器将预取以下行:0xF3040 和 0xF4040。无需预取 0xF1040 或 0xF2040,因为已经有对它们的请求。
  • 预取器不会预取到下一个 4KB 页面。所以如果这三个访问都是到页面的最后一行,预取器不会被触发。
  • 要跟踪的页面选择如下。每当需求负载或软件预取访问页面时,该页面将被跟踪,并将替换当前正在跟踪的 4 个页面之一。我没有进一步研究用于决定替换 4 页中的哪一页的算法。不过这可能很简单。
  • 当一个新页面因为上一个要点中提到的类型的访问而被跟踪时,至少需要另外两次访问同一页面和同一行来触发预取器进行预取下一行。否则,如果该行不存在,则对下一行的后续访问将在 L1 中丢失。之后,无论哪种方式,DCU 预取器的行为都如第二个和第三个要点中所述。例如,考虑以下三个需求负载未命中:0xF1040、0xF2048 和 0xF3004。对同一行有两次访问,第三次是对同一缓存集但不同行的访问。这些访问将使 DCU 预取器跟踪这两个页面,但它还不会触发它。当预取器看到对同一高速缓存集中的任何行的另外三个访问时,它将为当前正在跟踪的那些页面预取下一行。作为另一个示例,请考虑以下三个需求负载未命中:0xF1040、0xF2048 和 0xF3030。这些访问都指向同一行,因此它们不仅会使预取器跟踪页面,还会触发该页面和任何其他已被跟踪的页面的下一行预取。
  • 在我看来,预取器正在从正在访问的页面的页表条目(来自 TLB)接收脏标志。该标志指示页面是否脏。如果它是脏的,则预取器将不会跟踪该页面,并且对该页面的访问将不计入满足触发条件的三个访问中。所以看起来 DCU 预取器只是忽略了脏页。也就是说,尽管预取器支持该页面,但该页面不必是只读的。但是,需要进行更彻底的调查才能更准确地了解商店如何与 DCU 预取器交互。

因此触发预取器的访问不必是“升序”或遵循任何顺序。预取器似乎忽略了缓存行偏移量本身。只有物理页码很重要。

我认为 DCU 预取器有一个完全关联的缓冲区,其中包含 4 个条目。每个条目都标有(可能是物理的)页码,并有一个有效位来指示该条目是否包含有效的页码。此外,L1D 的每个缓存集都与一个 2 位饱和计数器相关联,每当需求负载或软件预取请求访问相应的缓存集并且未设置访问页面的脏标志时,该计数器就会递增。当计数器达到 3 时,触发预取器。预取器已经拥有需要从中预取的物理页码;它可以从与计数器对应的缓冲区条目中获取它们。因此,它可以立即向缓冲区跟踪的每个页面的下一个缓存行发出预取请求。但是,如果填充缓冲区不可用于触发的预取请求,则预取将被丢弃。然后计数器将被重置为零。但页表可能会被修改。每当刷新 TLB 时,预取器可能会刷新其缓冲区。

可能存在两个 DCU 预取器,每个逻辑核心一个。当超线程被禁用时,其中一个预取器也将被禁用。也可能是包含页码的 4 个缓冲区条目在两个逻辑内核之间静态分区并在禁用超线程时合并的情况。我不确定,但这样的设计对我来说很有意义。另一种可能的设计是每个预取器都有一个专用的 4 条目缓冲区。启用超线程时,不难确定 DCU 预取器的工作方式。我只是没有花精力研究它。

总而言之,DCU pefetcher 是现代高性能 Intel 处理器中可用的 4 种数据预取器中最简单的。似乎只有在顺序访问小块只读数据(如只读文件和静态初始化的全局数组)或同时访问多个可能包含许多小字段的只读对象时才有效,但速度较慢并跨越同一页面内的几个连续缓存行。

第 2.4.5.4 节还提供了有关 L1D 预取的一般信息,因此它适用于 DCU 预取器。

在以下情况下,加载操作会触发数据预取 条件满足:

  • 加载来自回写内存类型。

这意味着 DCU 预取器不会跟踪对 WP 和 WT 可缓存内存类型的访问。

  • 预取的数据与触发它的加载指令在同一个 4K 字节页面内。

这已经通过实验验证了。

  • 管道中没有围栏。

我不知道这是什么意思。见:https://software.intel.com/en-us/forums/software-tuning-performance-optimization-platform-monitoring/topic/805373

  • 正在进行的其他加载失败并不多。

只有 10 个填充缓冲区可以容纳错过 L1D 的请求。这提出了一个问题,如果只有一个可用的填充缓冲区,硬件预取器会使用它还是将其留给预期的需求访问?我不知道。

  • 没有源源不断的商店。

这表明,如果存在大量存储与少量负载交织在一起的流,则 L1 预取器将忽略负载并基本上暂时关闭,直到存储成为少数。但是,我的实验结果表明,即使是一个页面的单个存储也会关闭该页面的预取器。

所有英特尔凌动微架构都有 DCU 预取器。尽管预取器在这些微架构中跟踪的页面可能少于 4 个。

包括 Knights Landing 在内的所有 Xeon Phi 微架构都没有 DCU 预取器。我不知道后来的至强融核微架构。

【讨论】:

  • 管道中没有围栏。 我认为这意味着没有 StoreLoad 屏障(mfencelocked 指令)正在运行,等待所有待处理商店提交到 L1d。如果存在未决的 StoreLoad 屏障,则执行加载预取可能没有那么有用,因为可能必须重新获取可能过时的数据以满足屏障语义。它可能会引起额外的争用;屏障通常只用于与其他线程交互的代码中。
  • 感谢@Hadi Brais 的详细解释。我接受你的回答。您说过-当对同一缓存集中的一条或多条线进行三次或多次访问时,将触发 DCU 预取器。或者 DCU 预取器在两个连续高速缓存集中的一条或多条线的三个或更多访问时被触发。您能否给我一些提示或想法,以便我可以在我的系统中进行验证?
  • 我尝试过这种方式来验证 DCU 预取器是否在 3 次或更多次访问同一缓存集的缓存行后触发。这是我的方法 - (i) 我创建了 4KB 数组。 (ii) 访问 A[0] 一次,然后检查 A[16] 是否被预取。 (iii) 连续访问 A[0] 两次,然后检查 A[16] 是否被预取。 (iv)连续三次访问A[0]然后检查A[16]是否被预取。我期望 A[16] 应该在步骤 (iv) 处预取。
  • 在此链接manualsdir.com/manuals/733523/adlink-atca-6200a.html?page=55 中,它说,DCU 流媒体预取器在特定时间段内检测到对单个缓存行的多次读取,并选择将以下缓存行加载到 L1 数据缓存。
  • @PeterCordes 我尝试在训练预取器的指令序列和测试预取器的指令序列中插入mfencelfencelocked 指令。它们在代码中的存在似乎不会影响 DCU 预取器的行为。
猜你喜欢
  • 2017-07-31
  • 2015-05-28
  • 2020-10-26
  • 2017-01-31
  • 1970-01-01
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多