假设l1和l2高速缓存请求导致未命中,处理器是否会停滞直到访问主存储器?
我听说过切换到另一个线程的想法,如果可以的话,用来唤醒停滞线程的原因是什么?
假设l1和l2高速缓存请求导致未命中,处理器是否会停滞直到访问主存储器?
我听说过切换到另一个线程的想法,如果可以的话,用来唤醒停滞线程的原因是什么?
Answers:
内存延迟是计算机体系结构研究中研究的基本问题之一。
带有乱序指令问题的推测执行通常能够找到有用的工作来填补L1缓存命中期间的等待时间,但通常在10或20个周期左右后就耗尽了有用的工作。已经进行了一些尝试来增加长时间等待未命中期间可以完成的工作量。一种想法是尝试进行价值预测(Lipasti,Wilkerson和Shen,(ASPLOS-VII):138-147,1996)。这种想法在学术建筑研究界很流行了一段时间,但似乎在实践中不起作用。从历史上的垃圾箱保存值预测的最后一次尝试是超前执行(Mutlu,Stark,Wilkerson和Patt(HPCA-9):129,2003年)。在超前执行中,您意识到您的价值预测将是错误的,但无论如何都要进行推测性执行,然后根据该预测丢弃所有工作,并根据理论,您至少将对L2缓存进行一些预取错过。事实证明,冒名顶替浪费了太多能量,这是不值得的。
在这种情况下,可能会在业界引起一定关注的一种最终方法涉及创建非常长的重新排序缓冲区。指令是根据分支预测进行推测性执行的,但不会进行值预测。取而代之的是,所有依赖于长等待时间加载的指令都会在重新排序缓冲区中等待。但是,由于重排序缓冲区很大,因此如果分支预测变量执行得体,您可以继续获取指令,有时您将可以在指令流的后面找到有用的工作。该领域的一项有影响力的研究论文是连续流管道(Srinivasan,Rajwar,Akkary,Gandhi和Upton(ASPLOS-XI):107-119,2004年)。(尽管作者全部来自英特尔,但我相信这个想法在AMD上获得了更多关注。)
使用多个线程来进行延迟容忍已有很长的历史,在业界取得了更大的成功。所有成功的版本都使用硬件支持多线程。最简单(也是最成功的)版本就是通常所说的FGMT(细粒度多线程)或交错多线程。每个硬件内核都支持多个线程上下文(上下文本质上是寄存器状态,包括诸如指令指针之类的寄存器和任何隐式标志寄存器)。在细粒度的多线程处理器中,每个线程都在-订购。处理器跟踪哪些线程在长时延加载未命中时停滞,哪些线程准备好执行下一条指令,并且在每个周期上使用简单的FIFO调度策略选择执行该周期的就绪线程。大规模的早期示例是Burton Smith的HEP处理器(Burton Smith继续设计Tera超级计算机,它也是一种细粒度的多线程处理器)。但是我认为这个想法可以追溯到1960年代。
FGMT对流工作负载特别有效。所有现代GPU(图形处理单元)都是多核,其中每个核都是FGMT,该概念也广泛用于其他计算领域。Sun的T1也是多核FMGT,英特尔的Xeon Phi(该处理器通常仍称为“ MIC”,以前称为“ Larabee”)也是如此。
的想法同时多线程(Tullsen,埃格斯,和Levy),(ISCA-22:392-403,1995)相结合的硬件多线程与推测执行。处理器具有多个线程上下文,但是每个线程都是推测性执行的,并且无序执行。然后,更复杂的调度程序可以使用各种启发式方法从最有可能从事有用工作的线程中获取(Malik,Agarwal,Dhar和Frank,(HPCA-14:50-61),2008)。某大型半导体公司开始使用术语“ 超线程”来实现同时多线程,而该名称似乎是当今使用最广泛的名称。
重新阅读您的评论后,我意识到您也对处理器和内存之间发生的信号传递也很感兴趣。现代缓存通常允许同时处理多个未命中事件。这称为无锁缓存(Kroft,(ISCA-8):81-87,1981)。(但是该论文很难在网上找到,而且有点难以阅读。简短的回答:有很多簿记,但是您只是处理它。硬件簿记结构称为MSHR(丢失信息/状态保存寄存器) ),这就是Kroft在1981年的论文中给它起的名字。)
简短的答案是:没有,处理器停顿。
没有太多的可能性。切换到其他任务实际上并不是一个选择,原因有两个。这是一项昂贵的操作,并且由于当前任务和其他任务正在争用缓存中的空间,因此切换到其他任务本身可能需要主存储器访问,因此可能切换回原始任务。此外,这将必须涉及操作系统,因此处理器将必须触发某种形式的中断或陷阱 -实际上,处理器将切换至某些内核代码。
当处理器停止运行时,计时器继续运行,因此可能会有计时器中断,也可能有其他外设的中断。因此,与在高速缓存访问期间相比,上下文切换更有可能在主存储器访问期间发生,但这仅仅是因为它需要更长的时间。
尽管如此,现代计算机的确包含了多种技术,以减少处理器等待主内存所浪费的时间。停转确实会发生,但只有在无法避免的情况下才会发生。
一种技术是推测性获取:处理器尝试猜测将访问哪个内存位置,并获取它以提前缓存。例如,内存块上的循环很常见,因此,如果已为内存地址0x12340000、0x12340010和0x12340020加载了缓存行,则为0x12340030加载行可能是一个好主意。编译器可以通过生成预取指令来提供帮助,这些预取指令类似于加载,只是它们仅将数据从主存储器传输到缓存,而不传输到处理器寄存器。
另一种技术是投机执行。在执行加载之前,处理器开始执行下一条指令。由于指令的流水线化,这自然会发生。只有不依赖于加载值的指令才能通过这种方式执行:处理器必须执行依赖关系分析。对于条件指令(例如,负载r1;如果r1≠0,则分支),处理器采用分支预测试探法来猜测该值是多少。如果负载触发中止,可能需要重新考虑负载之后的推测执行。
诸如Itanium之类的某些体系结构通过默认情况下允许对指令进行重新排序来促进以方便的顺序执行指令:程序由非常长的指令字组成,而不是由一系列语义上依次执行的基本指令组成:单个指令包括许多操作将由处理器的不同组件并行执行。
切换到另一个线程发生在高端x86处理器上的超线程中。这是一种硬件设计技术:每个处理器内核包含两个单独的寄存器组(每个寄存器组对应于一个任务上下文),但是包含其他元素的单个实例,因此它可以支持两个独立的执行线程,但只能有效地从一个执行指令一个时间。当一个线程停止时,另一线程继续。从软件的角度来看,有两个独立的处理器。碰巧这些处理器在后台共享许多组件。
交换是内存高速缓存层次结构中的又一层:主内存可以看作是交换空间的高速缓存。通过交换,机制和性能比是不同的。如果任务需要通过交换加载数据,则加载指令将触发陷阱,该陷阱将执行内核代码以在RAM中分配页面并从磁盘加载其内容。发生这种情况时,内核可能会决定切换到另一个任务。
该问题的答案将随所讨论的体系结构而异。尽管许多CPU会因为切换线程花费的时间太长而停顿(ARM,没有超线程的x86等),但这并不是每种体系结构都采用的方法。在某些体系结构中,在CPU上调度的每个线程都有其自己的独立寄存器文件,因此处理器可以简单地从不等待内存访问的线程执行工作。据我了解,这在一定程度上是x86超线程的功能(仅使用2个线程),但是在GPGPU上它更为常见建筑。在CUDA的特定情况下,通常会在任何给定时间将至少几十个(即使不是数百个)线程扭曲加载到给定的多处理器上,每个线程(数百个或数千个)都有自己的寄存器。当给定线程发出内存访问时,这允许体系结构在下一个周期从另一个线程执行指令。因此,只要加载了足够多的线程,处理器核心就永远不会为内存访问而空闲。有关更多信息,请参见性能准则和内存层次结构。