让我们来看看所有这些意味着什么吧。如果CPU改变了发送给它的代码的意图,那是毫无用处的。坦白地说,如果双击文件变成执行一串格式化命令的话,那CPU就没有价值了。虽然那是一个极端的例子,但为了确保这样的事情不会发生,CPU必须遵循两个准则:
1. 指令必须按照程序最初的顺序进行编译(也就是通过CPU解释来找出它们正要求它做什
么)
2. 指令必须按照程序最初的顺序撤销(也就是每个操作的结果必须按照与它被发送到CPU
的相同顺序被写入内存/磁盘)
有序和无序构架两者都遵循这两个准则 - 在这两个阶段之间无序构架所做的处理不同。我们前面提到了有序构架不能对待执行指令重新排序。假如我们拥有一个有序CPU,它有一个加法器和一个载入/存储单元,它被要求执行下列代码(为了简单,我们在这里不讨论带有网络的情况):
1. LD R10,R11 2. ADD R5,R10,R10 3. ADD R9,R9,#1 4. ...
在第一条指令中,我们从存储在R11中的内存地址中载入数据到R10。然后,我们对刚从内存中取出的值做自加运算,并把结果存储在R5中。第三行,也是最后一行对存储在R9中的值赋予1的增量,并保存到R9中。快速浏览一下代码就发现,第二行不能在第一行之前执行。那样做将改变代码的目的(如果想要对某个值做自加运算,首先需要确保有这个值)。不过第三行是完全独立于第一跟第二行的。
对于有序微处理器,如果第一行要载入的数据就包含在缓存中的话,那么该指令将花费大约1 - 30个时钟周期来完成(根据构架和它所处的缓存级数而有所不同)。第二行在执行前将不得不等足那1 - 30个周期,然后到它执行了以后才轮到第三行。如果被请求的数据不是存储在缓存中的话(或许我们是第一次用到这个值,而我们又没有用到内存中存储在它附近的值),那我们有问题了。出乎意料地,第一行不是用大约1 - 30个周期来完成;现在它将用200个以上的时钟周期来完成。对于第二行倒没什么影响,因为它无论如
何都要等第一行完成了才能执行,但对于第三行来说,它可以很容易地在CPU正等着从内存中取出数据的时候执行。跟在第三行后面的任何独立指令也受到缓存失败的影响。
然而,至于无序微处理器,缓存失败的情况就大相廷径了。代码仍然按照顺序解码,意味着它以跟有序CPU相同的顺序获得指令1,2及3,但这时候我们有了在第一和第二行之前执行第三行的能力,而不是空等着第一行完成。如果发生缓存失败,这就给了无序微处理器一个相当大的性能优势,因为它不是在那等着浪费时钟周期而什么都不干。那么,无序CPU是怎么工作的呢?
如果有人以任何你想要的顺序让你做一系列的事情,那你只用简单地接受这个列表并完成它就行了。但如果他们又让你以他们规定的顺序报告返回你已经完成的事情,那你肯定要先把它们写下来,再重新组织它们为符合需要的顺序。
无序CPU的工作非常接近于同样的方法,只是以一个指令窗口代替了待办事项列表。指令窗口的功能类似于待办列表 - 它有所有以最初的顺序解码的指令,并保留了一个记录以确保这些指令按照它们被解码的顺序撤销。
在指令窗口旁边,无序CPU还有一个时序安排窗口 – 就是在这个“窗口”中完成所有的指令重新排序的。时序安排窗口包含了正确的推理来标出相关和独立的指令,并在等待相关指令为执行作准备的时候把所有的独立指令发送到执行单元。
当先前的相关指令(也就是等待来自主存中的数据或等待其它指令完成的指令)变成独立时,它们就能够被执行了,也是按照打乱的顺序的。
你可能立即会说,增加的指令窗口,时序安排窗口,所有检测独立指令的相关推理以及没有提到的处理无序执行但有序撤销的推理,所有这些导致了更复杂的微处理器。但对于无序微处理器还有另一个重要的问题 - 性能上的增长和指令级并行是非常依赖于指令窗口的大小的。
这个窗口安排得越大,就完全能够执行越多的并行,因为CPU面对着一个更广的指令集合,从中选出独立的指令。同时,窗口安排得越大,时钟速度就会越低。例如,Itanium有非常大的指令窗口来满足它的执行单元,而Pentium 4为了达到更高的时钟速度,只有一个小得多的窗口。
尽管有着这样那样的不足,但所有的流行x86微处理器无一例外地采用了无序核心,因为保持单核心构造简单在制造工艺提升时并不是应予优先考虑的事情。无序构架的好处有两方面:
1. 指令的动态重新排序让CPU回避了内存延时,可以获得更高的时钟速度。对于每次缓存
失败,Pentium 4 3.6GHz不得不等待大约230个时钟周期来从主存中取出数据,这在CPU看来是许多的空闲时间。能够在期间通过执行其它的独立指令来利用这个空闲时间,是像Pentium 4和Athlon 64这类构架逃脱它们如此高内存频率倍频的惩罚的一个方法。
2. 进一步增加指令级并行 - 通过对待执行指令的重新排序,无序构架能够在编译器无能为
力的地方改善ILP。
所以,AMD和Intel两者显然已经为通用x86微处理器计算过了,无序是最有意义的。那么,为什么Cell的设计者又重起炉灶,为处理器配备了9个独立的有序核心呢?
要回忆起的第一件事是从有序构架中能够获得相当可靠的性能。Itanium是有序微处理器,基于类似于Cell的设计,通过它编译器应该能够实现无序核心的那种并行。最近一代的Itanium核心运行在流行x86核心的一半速度下,然而CPU每个时钟能够执行大约两倍于最快的x86 CPU的指令。引用Intel的Justin Rattner关于Itanium的话说,“一个适当设计的指令集应该毫无问题地帮到有序构架。”所以,很可能相同的情况也适用于Cell...
五、Cell的方法 - 不带缓存的有序
记住,Cell的设计者在估计他们使用的每个晶体管导致的性能增长的同时设计了该处理器(有点夸张了,他们没有计算到2亿3千4百万个晶体管中的每一个,但他们非常严密地估计了每个构架上的决策)。在这样做的过程中,鉴于无序核心将增加的复杂性,有序vs.无序的思想肯定引起了巨大的争论。
由于无序的主要好处是减少了对内存延时的敏感性,因此Cell设计者们提出了另一个选择 - 具有可控制的(理解为可预知的)内存延时的有序核心怎么样?
有序微处理器有一个缺陷,就是一旦引入了缓存就不再能够控制内存延时了。大多数时候,设计良好的缓存将提供访问所需数据的低延时。但考虑Cell面对的应用程序类型(至少是最初的设计意图) - 3D渲染,游戏,物理学,媒体解码等等 - 所有的应用程序都不依赖大容量缓存。看看Intel任何一款拥有巨大缓存的CPU,注意到缓存超过一定数量以后,3D渲染,游戏和解码性能通常不会再有很大的提升了。例如,Pentium 4 660(3.60GHz - 2MB L2)在Business Winstone 2004中较Pentium 4 560(3.60GHz - 1MB L2)有13%的增长,但在3D游戏中帄均性能增长小于2%。在3dsmax中,完全没有由于额外的缓存造成的性能收益。在我们的媒体解码测试中同样可以看到类似的性能持帄。Playstation 3的使用模式不会是用来运行Microsoft Office的;它将与许多这类“媒体相关”的应用程序打交道,像3D游戏和媒体解码。对于这些应用程序类型而言,巨大的缓存是完全没有必要的 - 低延时内存访问是必需的,许多的内存带宽是重要的,但没有缓存也能获得这两样东西。那怎么做呢?Cell会说明怎么做的。
每个SPE配备了256KB的局部存储器,注意,并不是缓存。局部存储器是不会自行工作的。如果想要在其中存放点什么的话,需要发给SPE一条存储指令。缓存是自动起作用的;它使用与硬件紧密联系的算法来对它应该存储什么做出合理的推测。SPE的局部存储器大小与缓存相仿,但却像主存一样工作。另一个重要的方面是局部存储器是基于SRAM的,而不是基于DRAM的,所以就获得了与缓存类似的访问时间(对于SPE是6个周期),而不是主存访问时间(也就是上百个周期)。
那么重点是什么呢?虽然没有缓存,但引入了延时非常低的局部存储器,所以每个SPE有效地拥有了可控制,可预知的内存延时。这意味着一个聪明的开发者或巧妙的编译器能够为每个SPE极其精确地安排指令的时间。编译器会准确地知道,来自局部存储器的数据何时将准备好,从而能够围绕内存延时确定指令和操作的时间,就像无序微处理器那么好,但却没有附加的硬件复杂性。如果SPE需要存储在跟 Cell相连的主存中的数据,那么延时是可以预知的,这还是因为没有了缓存,不必担心事情会搞乱。
把 SPE做成有序核心对于它们的任务来说是很有意义的。然而,PPE做成有序则更多是受到空间/复杂性的限制。虽然SPE处理了更多指定的任务,但PPE在 Cell中的任务是处理所有的通用任务,那些在SPE的阵列上执行得并不理想。这个方法具有的问题是,为了像一个性能相对可靠的通用处理器那样运行,它需要缓存 - 而我们已经说明了缓存可能会怎样拖累有序核心了。如果Cell构架有薄弱环节的话,那就是PPE了,但要再次指出,Cell不是面向通用计算的,尽管有人可能会这样颠倒来用。