预取指令在退出之前是否需要返回其结果?

时间:2018-09-18 00:06:50

标签: performance x86 prefetch

在最近的Intel和AMD CPU上,可以执行一条已执行但请求行尚未到达指定缓存级别的预取指令吗?

就是说,预取是否因为似乎是针对负载而“阻塞”了,还是不阻塞?

1 个答案:

答案 0 :(得分:5)

关于Intel处理器,否。英特尔优化手册第7.3.3节提到了这一点:

  

PREFETCH可以提供比预加载更高的性能,因为:

     
      
  • 没有目标寄存器,它仅更新缓存行。
  •   
  • 如果这会导致错误,请不要完成自己的执行。
  •   
  • 不会停止正常的指令退役。
  •   
  • 不影响程序的功能行为。
  •   
  • 没有缓存行拆分访问。
  •   
  • 除非使用LOCK前缀,否则不会导致异常。 LOCK前缀不是与PREFETCH一起使用的有效前缀。
  •   
  • 如果这会导致错误,请不要完成自己的执行。
  •   
     

与预加载指令相比,PREFETCH的优点是特定于处理器的。将来可能会改变。

另外,第3.7.1节说:

  

软件PREFETCH操作的工作方式与从内存加载的方式相同   操作,但以下情况除外:

     
      
  • 从虚拟地址到物理地址的转换完成后,软件PREFETCH指令退出。
  •   
  • 如果需要异常(例如页面错误)来预取数据,则软件预取指令将退出而不会   预取数据。
  •   

我已经在Haswell和Broadwell上通过实验验证了这两点。

enter image description here

enter image description here

所有未命中的TLB:所有预取指令都未命中所有MMU和数据缓存,但是页面在主内存中(没有次要或主要的页面错误)。

所有命中的TLB:所有预取指令都命中L1 TLB和数据缓存。

对不同的页面进行故障处理:所有预取指令均会丢失所有MMU和数据缓存,并且页面描述符会导致页面故障。每个预取指令都访问不同的虚拟页面。

同一页故障:所有预取指令都丢失所有MMU和数据高速缓存,并且页描述符导致页故障。每个预取指令都访问相同的虚拟页面。

对于Broadwell图,将显示PREFETCH0PREFETCHW的结果。 Haswell不支持PREFETCHW。 Haswell和Broadwell的频率分别固定为3.4GHz和1.7GHz,我在两者上均使用了intel_pstate功率缩放驱动器。所有硬件预取器均已打开。请注意,PREFETCHW在页面错误上的等待时间与目标页面是否可写无关。只读页面导致的错误与由于任何其他原因引起的错误具有相同的影响。另外,我的实验仅考虑没有内核具有缓存行副本的情况。

由于1c依赖关系链,预期1周期的吞吐量:

loop:
prefetcht0 (%rax)
add    $0x1000,%rax 
cmp    %rbx,%rax
jne    loop

在Broadwell上,“故障相同页面”情况似乎比“故障不同页面”情况稍慢。这与Haswell相反。我不知道为什么这可能取决于分页结构的级别,分页结构包含无效的条目,基本上页面walker会在该条目上检测到页面错误。这取决于操作系统。

我认为预取指令之所以不能立即在TLB未命中而退出的原因是,加载单元没有像存储单元那样的退出后逻辑。这里的想法是,由于最有可能在预取之后对页面进行需求访问(这可能是为什么存在预取的原因),所以无论是在需求访问还是在预取中,由于TLB未命中都会造成停顿。尤其是当紧随预取之后的指令无法访问同一页面时,在预取上停顿可能会更好。

此外,我已经通过实验验证了预取指令可以在预取操作完成之前退出,方法是在预取指令后放置LFENCE并观察每个预取指令的时间仅略微增加(栅栏的成本)与使用负载而不是预取相比。

至强融核处理器上的软件预取指令的执行方式与Haswell / Broadwell 1 上的执行方式相同,但还请阅读下面有关Itanium的部分。

第7.3.3节还说:

  

在某些情况下,PREFETCH将不执行数据预取。   这些包括:

     
      
  • 在较旧的微体系结构中,将导致数据转换后备缓冲区(DTLB)未命中的PREFETCH被丢弃。在基于   Nehalem,Westmere,Sandy Bridge和更新的微体系结构,英特尔   核心2处理器和Intel Atom处理器,导致DTLB的PREFETCH   可以跨页面边界获取未命中的内容。
  •   
  • 对导致故障/异常的指定地址的访问。
  •   
  • PREFETCH的目标是不可缓存的内存区域(例如USWC和UC)。
  •   
  • 如果内存子系统耗尽了第一级缓存和第二级缓存之间的请求缓冲区。
  •   
  • 使用LOCK前缀。这将导致无效的操作码异常。
  •   

第二点已经在Haswell,Broadwell和Skylake上进行了实验验证。我的代码无法检测到指出LFB不足时可以丢弃预取请求的第四点。 AMD results似乎表明AMD也删除了预取请求。但是,AMD每次访问的时间仍然比Intel短得多。我认为AMD在TLB填充缓冲区已满时会丢弃预取请求,而Intel在L1D填充缓冲区已满时会丢弃预取请求。我的代码从未使L1D填充缓冲区满,这解释了AMD与Intel的结果。

第一点说,在Core2和Atom微体系结构以及更高版本上,在TLB未命中时不会丢弃软件预取。根据优化手册的旧版本,型号为3或更大的Pentium 4处理器也不会在TLB未命中时放弃软件预取。在英特尔酷睿微体系结构和(某些)奔腾M处理器上也可能是这种情况(我无法找到有关这些处理器的英特尔资源)。型号小于3的Pentium III处理器和Pentium 4处理器肯定会在TLB未命中时丢弃软件预取。奔腾III之前的处理器不支持软件预取指令。


Prefetch uops被调度到端口2或3,并在加载缓冲区中分配。不会将预取uops放到同一缓存行。也就是说,每个uop将获得自己的加载缓冲区。我认为(但我尚未通过实验验证)ROB条目已分配给预取uops。只是ROB永远不会因预取uops而停顿,只要它们已被调度到加载端口即可。

预取请求本身(发送到L1d或高速缓存的外部级别)本身并不是预取uop在ROB中标记为完成并准备退休之前必须等待的东西,这与常规加载不同。


有一个有趣的2011 patent,讨论了对Itanium 2 处理器上软件预取的增强。它提到,以前的Itanium处理器必须在软件预取错过了TLB时停顿,因为它们被设计为不会丢弃任何软件预取请求,而以后的指令则不能继续执行,因为它们是有序处理器。该专利提出了一种设计,该设计允许软件预取请求相对于后续指令无序执行而不会丢弃它们。这是通过添加数据预取队列(DPQ)来完成的,该数据预取队列用于排队错过TLB的软件预取请求。硬件页表遍历完成后,然后重新发出DPQ中的预取。另外,添加了多个硬件页表遍历器,即使它们错过了TLB,也可能允许以后的需求访问执行。但是,如果DPQ充满了预取指令,则流水线将停在下一条预取指令上。同样根据该专利,即使在页面错误时也不会丢弃软件预取请求。这与大核心和至强披披形成鲜明对比。该专利还讨论了在Itanium中实现的硬件预取器。

在乱序的大核心微体系结构中,负载缓冲区自然扮演着DPQ的角色。我不知道至强披披是否有这样的结构。


AMD优化手册5.6节中的内容如下:

  

预取指令可能受到以下错误依赖的影响:   商店。如果某个商店的地址与请求相匹配,则表明   请求(预取指令)可能被阻止,直到存储   写入缓存。因此,代码应预取   距离周围商店的数据至少64个字节   地址。

我很好奇,可以通过放置两个预取指令和一个存储指令(后跟一个虚拟add rax, rax)在Intel处理器(在Haswell上)进行测试,并且观察到以下内容:

  • UOPS_RETIRED.STALL_CYCLES明显大于核心周期数,这没有任何意义。
  • 发送到端口2和3的uops总数比预期的高出约16%。这表明正在重播预取联播。
  • RESOURCE_STALLS.ANY报告基本上没有停顿。这与以下情况形成对比:先有两条预取指令,然后是两条伪ALU指令(流水线停在加载缓冲区上)。

但是,只有在存储与预取指令位于同一4K页面时,我才观察到这些影响。如果存储在另一个页面上,则代码的工作方式类似于带有两个虚拟ALU的代码。因此,似乎商店与Intel处理器上的预取指令交互。


(1)但是它们与硬件预取器的交互方式不同。但是,这是退休后的效果。

(2)Itanium是IA-64处理器家族,因此与问题不完全相关。