预取指令是否需要 return 它们的结果才能退出?
Do prefetch instructions need to return their result before they retire?
在最近的 Intel 和 AMD CPU 上,已经执行但请求行尚未到达指定缓存级别的预取指令是否仍然可以退出?
也就是说,预取的退出 "blocking" 是因为它看起来是为了加载,还是非阻塞的?
关于英特尔处理器,没有。这个在Intel优化手册Section 7.3.3中有提到:
PREFETCH can provide greater performance than preloading because:
- Has no destination register, it only updates cache lines.
- Does not complete its own execution if that would cause a fault.
- Does not stall the normal instruction retirement.
- Does not affect the functional behavior of the program.
- Has no cache line split accesses.
- Does not cause exceptions except when the LOCK prefix is used. The LOCK prefix is not a valid prefix for use with PREFETCH.
- Does not complete its own execution if that would cause a fault.
The advantages of PREFETCH over preloading instructions are processor specific. This may change in the future.
此外第 3.7.1 节说:
Software PREFETCH operations work the same way as do load from memory
operations, with the following exceptions:
- Software PREFETCH instructions retire after virtual to physical address translation is completed.
- If an exception, such as page fault, is required to prefetch the data, then the software prefetch instruction retires without
prefetching data.
我已经在 Haswell 和 Broadwell 上通过实验验证了这两点。
所有未命中 TLB:所有预取指令未命中所有 MMU 和数据高速缓存,但页面位于主内存中(没有次要或主要页面错误)。
全部命中 TLB:所有预取指令命中 L1 TLB 和数据缓存。
不同页面出现故障:所有预取指令都未命中所有 MMU 和数据缓存,页面描述符导致页面错误。每个预取指令访问不同的虚拟页面。
同一页面错误:所有预取指令都未命中所有 MMU 和数据高速缓存,页面描述符导致页面错误。每个预取指令访问相同的虚拟页面。
对于 Broadwell 图,显示了 PREFETCH0
和 PREFETCHW
的结果。 PREFETCHW
在 Haswell 上不受支持。 Haswell 和 Broadwell 的频率分别固定为 3.4GHz 和 1.7GHz,我在两者上都使用了 intel_pstate power scaling 驱动程序。所有硬件预取器都已打开。请注意,页面错误 PREFETCHW
的延迟与目标页面是否可写无关。只读页面导致的故障与任何其他原因造成的故障具有相同的影响。另外,我的实验只考虑了没有核心有缓存行副本的情况。
由于 1c 依赖链,预计 1 周期吞吐量:
loop:
prefetcht0 (%rax)
add [=10=]x1000,%rax
cmp %rbx,%rax
jne loop
在 Broadwell 上,"fault same page" 的情况似乎比 "fault different pages" 的情况稍慢。这与哈斯韦尔形成鲜明对比。我不知道为什么。这可能取决于包含无效条目的分页结构的级别,页面遍历器基本上在该级别检测到页面错误。这是 OS 相关的。
我认为预取指令不能在 TLB 未命中时立即退出的原因是因为加载单元没有像存储单元那样的 post-退出逻辑。这里的想法是,由于在预取之后很可能会有对页面的请求访问(这可能是预取存在的原因),无论如何在请求访问或预取时都会由于 TLB 未命中而出现停顿。也许在预取上停止更好,尤其是当紧随预取之后的指令不访问同一页面时。
此外,我已经通过实验验证预取指令可以在预取操作完成之前退出,方法是在预取指令之后放置一个 LFENCE
并观察每个时间与使用加载而不是预取相比,预取指令仅略微增加(栅栏成本)。
Xeon Phi 处理器上的软件预取指令与 Haswell/Broadwell 1 上的执行方式相同,但另请阅读下面有关 Itanium 的部分。
第 7.3.3 节还说:
There are cases where a PREFETCH will not perform the data prefetch.
These include:
- In older microarchitectures, PREFETCH causing a Data Translation Lookaside Buffer (DTLB) miss would be dropped. In processors based on
Nehalem, Westmere, Sandy Bridge, and newer microarchitectures, Intel
Core 2 processors, and Intel Atom processors, PREFETCH causing a DTLB
miss can be fetched across a page boundary.
- An access to the specified address that causes a fault/exception.
- PREFETCH targets an uncacheable memory region (for example, USWC and UC).
- If the memory subsystem runs out of request buffers between the first-level cache and the second-level cache.
- The LOCK prefix is used. This causes an invalid opcode exception.
第二点已经在Haswell、Broadwell、Skylake上进行了实验验证。我的代码无法检测到当 运行 超出 LFB 时可以丢弃预取请求的第四点。 AMD results 似乎表明 AMD 也放弃了预取请求。但 AMD 的每次访问时间仍然比 Intel 小得多。我认为 AMD 在 TLB 填充缓冲区已满时丢弃预取请求,而英特尔在 L1D 填充缓冲区已满时丢弃它们。我的代码从未使 L1D 填充缓冲区变满,这解释了 AMD 与 Intel 的结果。
第一点指出,在 Core2 和 Atom 微架构及更高版本中,软件预取不会因 TLB 未命中而丢失。根据旧版本的优化手册,型号为 3 或更大的奔腾 4 处理器也不会在 TLB 未命中时丢弃软件预取。 Intel Core 微体系结构和(某些)Pentium M 处理器也可能是这种情况(我无法找到有关这些处理器的 Intel 资源)。型号小于 3 的奔腾 III 处理器和奔腾 4 处理器肯定会在 TLB 未命中时放弃软件预取。 Pentium III 之前的处理器不支持软件预取指令。
预取微指令被分派到端口 2 或 3 并分配到加载缓冲区中。预取 uops 到同一缓存行不会合并。也就是说,每个 uop 将获得自己的加载缓冲区。我认为(但我还没有通过实验验证)ROB 条目是为预取微指令分配的。只是 ROB 永远不会在预取 uops 上停止,只要它们已被分派到加载端口。
与常规加载不同,预取请求本身(发送到 L1d 或缓存的外层)不是预取 uop 在被标记为已完成并准备退出之前在 ROB 中必须等待的东西。
2011 年有一篇有趣的文章 patent 讨论了 Itanium2 处理器上软件预取的增强功能。它提到以前的 Itanium 处理器在软件预取错过 TLB 时不得不停止,因为它们被设计为不丢弃任何软件预取请求,并且以后的指令无法继续通过它,因为它们是有序处理器。该专利提出了一种设计,允许软件预取请求在不丢弃后续指令的情况下乱序执行。这是通过添加数据预取队列 (DPQ) 来完成的,该队列用于对错过 TLB 的软件预取请求进行排队。然后在硬件页面 table 遍历完成后重新发出 DPQ 中的预取。此外,添加了多个硬件页面 table walker,以潜在地允许以后的请求访问执行,即使它们错过了 TLB。然而,如果 DPQ 被预取指令填满,流水线将在下一个预取指令处停止。同样根据该专利,软件预取请求即使在出现页面错误时也不会被丢弃。这与大核和 Xeon Phi 形成对比。该专利还讨论了在 Itanium 中实现的硬件预取器。
在无序的大核微架构中,负载缓冲器自然扮演着DPQ的角色。不知道Xeon Phi有没有这样的结构
AMD 优化手册第 5.6 节说明如下:
The prefetch instructions can be affected by false dependencies on
stores. If there is a store to an address that matches a request, that
request (the prefetch instruction) may be blocked until the store is
written to the cache. Therefore, code should prefetch data that is
located at least 64 bytes away from any surrounding store’s data
address.
我很好奇,通过放置两个预取指令和一个存储指令(后跟一个虚拟 add rax, rax
)在英特尔处理器(在 Haswell 上)上测试它,我观察到以下内容:
UOPS_RETIRED.STALL_CYCLES
明显大于核心循环数,这没有意义。
- 发送到端口 2 和 3 的 uops 总数比预期的高出约 16%。这表明正在重放预取微指令。
RESOURCE_STALLS.ANY
报道基本没有档口。这与有两个预取指令后跟两个虚拟 ALU 指令(流水线在加载缓冲区上停止)的情况形成对比。
但是,只有当存储与预取指令相同的 4K 页面时,我才观察到这些效果。如果商店位于不同的页面,则代码的工作方式类似于具有两个虚拟 ALU 的代码。因此,存储似乎与 Intel 处理器上的预取指令交互。
(1) 但它们与硬件预取器的交互方式不同。然而,这是一种 post 退休效应。
(2) Itanium 是 IA-64 处理器系列,因此与问题不完全相关。
在最近的 Intel 和 AMD CPU 上,已经执行但请求行尚未到达指定缓存级别的预取指令是否仍然可以退出?
也就是说,预取的退出 "blocking" 是因为它看起来是为了加载,还是非阻塞的?
关于英特尔处理器,没有。这个在Intel优化手册Section 7.3.3中有提到:
PREFETCH can provide greater performance than preloading because:
- Has no destination register, it only updates cache lines.
- Does not complete its own execution if that would cause a fault.
- Does not stall the normal instruction retirement.
- Does not affect the functional behavior of the program.
- Has no cache line split accesses.
- Does not cause exceptions except when the LOCK prefix is used. The LOCK prefix is not a valid prefix for use with PREFETCH.
- Does not complete its own execution if that would cause a fault.
The advantages of PREFETCH over preloading instructions are processor specific. This may change in the future.
此外第 3.7.1 节说:
Software PREFETCH operations work the same way as do load from memory operations, with the following exceptions:
- Software PREFETCH instructions retire after virtual to physical address translation is completed.
- If an exception, such as page fault, is required to prefetch the data, then the software prefetch instruction retires without prefetching data.
我已经在 Haswell 和 Broadwell 上通过实验验证了这两点。
所有未命中 TLB:所有预取指令未命中所有 MMU 和数据高速缓存,但页面位于主内存中(没有次要或主要页面错误)。
全部命中 TLB:所有预取指令命中 L1 TLB 和数据缓存。
不同页面出现故障:所有预取指令都未命中所有 MMU 和数据缓存,页面描述符导致页面错误。每个预取指令访问不同的虚拟页面。
同一页面错误:所有预取指令都未命中所有 MMU 和数据高速缓存,页面描述符导致页面错误。每个预取指令访问相同的虚拟页面。
对于 Broadwell 图,显示了 PREFETCH0
和 PREFETCHW
的结果。 PREFETCHW
在 Haswell 上不受支持。 Haswell 和 Broadwell 的频率分别固定为 3.4GHz 和 1.7GHz,我在两者上都使用了 intel_pstate power scaling 驱动程序。所有硬件预取器都已打开。请注意,页面错误 PREFETCHW
的延迟与目标页面是否可写无关。只读页面导致的故障与任何其他原因造成的故障具有相同的影响。另外,我的实验只考虑了没有核心有缓存行副本的情况。
由于 1c 依赖链,预计 1 周期吞吐量:
loop:
prefetcht0 (%rax)
add [=10=]x1000,%rax
cmp %rbx,%rax
jne loop
在 Broadwell 上,"fault same page" 的情况似乎比 "fault different pages" 的情况稍慢。这与哈斯韦尔形成鲜明对比。我不知道为什么。这可能取决于包含无效条目的分页结构的级别,页面遍历器基本上在该级别检测到页面错误。这是 OS 相关的。
我认为预取指令不能在 TLB 未命中时立即退出的原因是因为加载单元没有像存储单元那样的 post-退出逻辑。这里的想法是,由于在预取之后很可能会有对页面的请求访问(这可能是预取存在的原因),无论如何在请求访问或预取时都会由于 TLB 未命中而出现停顿。也许在预取上停止更好,尤其是当紧随预取之后的指令不访问同一页面时。
此外,我已经通过实验验证预取指令可以在预取操作完成之前退出,方法是在预取指令之后放置一个 LFENCE
并观察每个时间与使用加载而不是预取相比,预取指令仅略微增加(栅栏成本)。
Xeon Phi 处理器上的软件预取指令与 Haswell/Broadwell 1 上的执行方式相同,但另请阅读下面有关 Itanium 的部分。
第 7.3.3 节还说:
There are cases where a PREFETCH will not perform the data prefetch. These include:
- In older microarchitectures, PREFETCH causing a Data Translation Lookaside Buffer (DTLB) miss would be dropped. In processors based on Nehalem, Westmere, Sandy Bridge, and newer microarchitectures, Intel Core 2 processors, and Intel Atom processors, PREFETCH causing a DTLB miss can be fetched across a page boundary.
- An access to the specified address that causes a fault/exception.
- PREFETCH targets an uncacheable memory region (for example, USWC and UC).
- If the memory subsystem runs out of request buffers between the first-level cache and the second-level cache.
- The LOCK prefix is used. This causes an invalid opcode exception.
第二点已经在Haswell、Broadwell、Skylake上进行了实验验证。我的代码无法检测到当 运行 超出 LFB 时可以丢弃预取请求的第四点。 AMD results 似乎表明 AMD 也放弃了预取请求。但 AMD 的每次访问时间仍然比 Intel 小得多。我认为 AMD 在 TLB 填充缓冲区已满时丢弃预取请求,而英特尔在 L1D 填充缓冲区已满时丢弃它们。我的代码从未使 L1D 填充缓冲区变满,这解释了 AMD 与 Intel 的结果。
第一点指出,在 Core2 和 Atom 微架构及更高版本中,软件预取不会因 TLB 未命中而丢失。根据旧版本的优化手册,型号为 3 或更大的奔腾 4 处理器也不会在 TLB 未命中时丢弃软件预取。 Intel Core 微体系结构和(某些)Pentium M 处理器也可能是这种情况(我无法找到有关这些处理器的 Intel 资源)。型号小于 3 的奔腾 III 处理器和奔腾 4 处理器肯定会在 TLB 未命中时放弃软件预取。 Pentium III 之前的处理器不支持软件预取指令。
预取微指令被分派到端口 2 或 3 并分配到加载缓冲区中。预取 uops 到同一缓存行不会合并。也就是说,每个 uop 将获得自己的加载缓冲区。我认为(但我还没有通过实验验证)ROB 条目是为预取微指令分配的。只是 ROB 永远不会在预取 uops 上停止,只要它们已被分派到加载端口。
与常规加载不同,预取请求本身(发送到 L1d 或缓存的外层)不是预取 uop 在被标记为已完成并准备退出之前在 ROB 中必须等待的东西。
2011 年有一篇有趣的文章 patent 讨论了 Itanium2 处理器上软件预取的增强功能。它提到以前的 Itanium 处理器在软件预取错过 TLB 时不得不停止,因为它们被设计为不丢弃任何软件预取请求,并且以后的指令无法继续通过它,因为它们是有序处理器。该专利提出了一种设计,允许软件预取请求在不丢弃后续指令的情况下乱序执行。这是通过添加数据预取队列 (DPQ) 来完成的,该队列用于对错过 TLB 的软件预取请求进行排队。然后在硬件页面 table 遍历完成后重新发出 DPQ 中的预取。此外,添加了多个硬件页面 table walker,以潜在地允许以后的请求访问执行,即使它们错过了 TLB。然而,如果 DPQ 被预取指令填满,流水线将在下一个预取指令处停止。同样根据该专利,软件预取请求即使在出现页面错误时也不会被丢弃。这与大核和 Xeon Phi 形成对比。该专利还讨论了在 Itanium 中实现的硬件预取器。
在无序的大核微架构中,负载缓冲器自然扮演着DPQ的角色。不知道Xeon Phi有没有这样的结构
AMD 优化手册第 5.6 节说明如下:
The prefetch instructions can be affected by false dependencies on stores. If there is a store to an address that matches a request, that request (the prefetch instruction) may be blocked until the store is written to the cache. Therefore, code should prefetch data that is located at least 64 bytes away from any surrounding store’s data address.
我很好奇,通过放置两个预取指令和一个存储指令(后跟一个虚拟 add rax, rax
)在英特尔处理器(在 Haswell 上)上测试它,我观察到以下内容:
UOPS_RETIRED.STALL_CYCLES
明显大于核心循环数,这没有意义。- 发送到端口 2 和 3 的 uops 总数比预期的高出约 16%。这表明正在重放预取微指令。
RESOURCE_STALLS.ANY
报道基本没有档口。这与有两个预取指令后跟两个虚拟 ALU 指令(流水线在加载缓冲区上停止)的情况形成对比。
但是,只有当存储与预取指令相同的 4K 页面时,我才观察到这些效果。如果商店位于不同的页面,则代码的工作方式类似于具有两个虚拟 ALU 的代码。因此,存储似乎与 Intel 处理器上的预取指令交互。
(1) 但它们与硬件预取器的交互方式不同。然而,这是一种 post 退休效应。
(2) Itanium 是 IA-64 处理器系列,因此与问题不完全相关。