2014-08-29 61 views
4

假设l1和l2缓存请求导致未命中,处理器会一直停滞,直到访问主内存为止?处理器在等待主内存提取时做了什么

我听说过切换到另一个线程的想法,如果是这样,用什么来唤醒停顿的线程?

+0

也许这个问题应该去http://cs.stackexchange.com/ – 2014-08-29 07:30:05

+0

我的理解是,超线程CPU可以执行其他任务,而内存正在被提取。此外,其他线程可以执行任意数量的操作,只要它们不使用由内存提取操作占用的总线。实际上,今天大多数内存总线都是串行的(这是否正确?),所以只有一个CPU可能一次能够访问主内存。 – arman 2014-08-29 07:55:15

+0

*我听说过切换到另一个线程的想法*根据语言,可以使用'std :: thread'(在C++中)切换到另一个线程。移动到另一个线程所需的时间以及将L1/L2内存复制到该线程所需的时间可能会超过典型的缓存行抓取时间,所以我不认为这是自动修复。 – arman 2014-08-29 07:58:04

回答

1

一个现代的无序处理器有一个重排序缓冲区(ROB),它可以跟踪所有的飞行指令并保持程序顺序。一旦ROB头部的指令完成,它将从ROB中清除。现代ROB是大约100-200个条目。同样,一个现代的OoO处理器有一个加载/存储队列,用于跟踪所有内存指令的状态。

最后,获取并解码但尚未执行的指令位于称为“问题队列/窗口”(或“保留站”)的地方,具体取决于设计人员的术语,这个问题在很大程度上与这个问题无关)。处于“问题队列”中的指令具有它们所依赖的寄存器操作数的列表,以及它们的操作数是否“繁忙”。一旦它们的所有寄存器操作数不再繁忙,指令就准备好执行并且它请求被“发出”。

问题调度程序从已准备好的指令中选取并将它们发给执行单元(这是无序的部分)。

让我们看看下面的顺序:

addi x1 <- x2 + x3 
ld x2 0(x1) 
sub x3 <- x2 - x4 

正如我们所看到的,“子”指令依赖于先前加载指令(由寄存器“X2”的方式)。加载指令将被发送到内存并在高速缓存中未命中。它可能需要100多个周期才能返回并将结果写回到x2。同时,子指令将被放置在问题队列中,其操作数“x2”被标记为繁忙。它会坐在那里等待很长很长的时间。 ROB将迅速填满预测的指令,然后停止。整个核心将停止并旋转它的拇指。

一旦加载返回,它将写回“x2”,将这个事实广播到问题队列中,子听到“x2现在已准备就绪!”。并且子终端可以最终进行,ld指令最终可以提交,并且ROB将开始清空,从而可以提取新指令并将其插入到ROB中。


很明显,这会导致一个空闲的管道,因为很多指令都会被备份,等待负载返回。这有几个解决方案。

一个想法是简单地将整个线程切换到新线程。在一个简单的解释中,这基本上意味着清空整个流水线,将线程的PC(指向加载指令)和提交的寄存器文件的状态(在加载指令之前的加载)。通过缓存未命中来安排新线程有很多工作要做。呸。

另一种解决方案是同时多线程。对于双向SMT机器,您有两台PC和两个架构寄存器文件(即,您必须复制每个线程的架构状态,但可以共享微架构资源)。通过这种方式,一旦您为特定线程提取并解码指令,它们对于后端来说就是一样的。因此,尽管“子”指令会一直等待问题队列中的负载回来,但另一个线程可以继续前进。随着第一个线程停顿,可以为第二个线程分配更多资源(获取带宽,解码带宽,问题带宽等)。通过这种方式,管道通过毫不费力地填充第二个线程而保持繁忙。

3

在现代CPU中同时存在很多很多事情。当然,任何需要内存访问的结果都无法继续,但可能还有很多事情要做。假设以下C代码:

double sum = 0.0; 
for (int i = 0; i < 4; ++i) sum += a [i]; 

if (sum > 10.0) call_some_function(); 

并假设读取数组a失速。由于读取[0]失速,加法和+ = a [0]将失速。但是,处理器继续执行其他指令。就像增加我一样,检查我是否正在循环读取一个[1]。这也是停顿,第二个加法和= + a [1]失速 - 这次是因为既没有正确的总和值也没有知道值a [1],但是事情继续进行,最终代码达到了语句“if(和> 10.0)“。

此时处理器不知道总和是多少。然而,它可以根据以前的分支发生的情况猜测结果,并开始执行call_some_function()推测性的函数。所以它会继续运行,但要小心:当call_some_function()将内容存储到内存中时,它不会发生。

最终读取[0]成功后,许多周期后。当发生这种情况时,它将被加入总和,然后将[1]加到总和中,然后a [2],然后a [3],然后比较总和> 10.0将正确执行。然后决定分支将变得正确或不正确。如果不正确,call_some_function()的所有结果都将被丢弃。如果正确,call_some_function()的所有结果都会从推测结果转换为实际结果。

如果失速时间过长,处理器将最终用完所有的事情。它可以很容易地处理四个增加和一个无法执行的比较,但最终它太多了,处理器必须停止。然而,在一个超线程系统中,你有另一个线程可以继续快乐地运行,并且以更高的速度运行,因为没有其他人使用内核,所以整个内核仍然可以继续执行有用的工作。

+0

我认为你选择了一个很好的例子。这种投机行为是我没有意识到的。你有什么好的参考? – 2014-09-03 11:20:21

+2

这里有一篇关于英特尔Haswell处理器的优秀文章:http://www.realworldtech.com/haswell-cpu/,它解释了现代处理器为提高速度所做的大量工作。维基百科文章http://en.wikipedia.org/wiki/Speculative_execution会有所帮助。 – gnasher729 2014-10-14 20:36:57

+0

尼斯裁判感谢。投机执行是我没有从你的原始解释中吸取的魔法词! – 2014-10-15 09:31:59

相关问题