57游戏  www.57yx.com  -  最全面的游戏资讯门户
热门游戏:永恒之塔 SD敢达 伊苏战记 开心online
当前位置 :| 主页>电视游戏>PS3>

详细解读PS3多核心Cell处理器

来源:TGBUS 作者: 时间:2008-10-23

  第一行写入R10,同时第二行从R10中读取。在没有上下文的情况下,CPU可能在第一行完成前开始执行第二行 - 对第三和第二行的情况也一样。在这里我们遇到的是所谓的“写入后读取”(Read After Write,RAW)相关性。有许多类型的相关性,但理解了这个基础的例子就已经足以让我们进入即将到来的下一个话题了 - 这种相关性的影响。

  相关性带来的问题是它限制了并行执行的能力。以Athlon 64为例。它有三个整数执行单元,所有这些执行我们上面使用的代码的能力是相等的。理论上Athlon 64能够同时执行并行的三行整数操作 - 假定操作之间不存在相关性。执行上面的代码时,Athlon 64整数执行单元中的两个将闲置,要等待代码的第一行被执行。

  像我们上面讨论的这种简单的相关性,阻碍了现代流行微处理器发挥它们最佳性能的能力。它仿佛有三只手,但只能通过一次收拾一件物品来打扫房间;这样低下的效率真是令人沮丧啊。

  围绕相关性调整指令

  幸好,对代码中的相关性问题有多种解决办法;一是从硬件入手解决问题,另一个是在软件中解决问题。

  软件编译器负责产生发送到CPU用于执行的汇编代码。因此,由于CPU内部工作的固有原理,一般来说编译器能够产生最少数据相关性的代码。

  有微处理器构架是完全依靠编译器来实现指令级并行的,同时尽可能多地消除相关性。这些构架被认为是有序微处理器。

  有序构架

  顾名思义,有序微处理器只能按照指令被发送到CPU的顺序来执行它们。CPU最多可以并行执行多条指令,但它没有能力对指令重新排序,以更好地适应它的需要。

  如果编译器足够好的话,那有序微处理器应该是优秀的。不过有两个关键的限制:

  1. 对有序构架的二进制编译是非常依赖构架的

  虽然Athlon 64和Pentium 4两者都完全能够运行x86代码,但它们采用了极其不同的微构架,具有不同的执行单元,并且各自擅长的领域有很大的差异。如果上述两个芯片都完全依靠编译器来实现并行和最大化性能的话,肯定有一个会遭受极大的损失。即使每个程序总会有两个版本,但那趋向于变得巨大而杂乱 - 特别是从升级/补丁的观点来看。编译器不得不密切关注着它正为之编译的构架,就算它像游戏控制台一样工作,没有众多厂商以共同的ISA提供不同构架的 CPU,但还是不如桌面x86市场的好。

  2. 不可预知的内存延时

  缓存在绝大多数时候是个好东西。微处理器上的缓存尽力保存了频繁使用的数据,所以它应该被做得与CPU通信的延时非常低。问题是缓存增加了一个不可预知的等级到它从内存获取数据的时间上。一次缓存冲突可能意味着数据要在10 - 20个周期后到达。一次缓存失败可能意味着好几百个周期的延误。至于有序微处理器,它不能根据数据可用性对指令重新排序,那么如果数据在缓存中不可用的话,CPU就不得不等待更长的时间以把它从主存中取出来,整个CPU不得不闲置,直到那个数据被从主存中读出。即使有其它的指令能够被执行,有序微处理器也无法有效处理待执行指令的重新排序,以克服不可预知的内存延时。

  如果找到办法解决有序构架的限制的话,会有一些非常实际的好处:

  1. 微处理器设计大为简化

  为了处理待执行指令的重新排序,无序微处理器增加了数目惊人的复杂单元。我们将在下一节中更详细地讨论它们。通过把这个复杂度转移到软件/编译器那边,极大地简化了微处理器的复杂性,并为其它能够产生更好性能收益的单元剩下了晶体管额度。越低的复杂性也意味着越小的功耗和发热量。

  2. 更短的管线

  一般来说,为了处理指令的重新排序,构架的管线级数不得不增加,导致了更高的功耗,并且(由于更高的分支预报损失)而需要更精确的分支预报器。虽然对于较长的管线设计而言,增加管线深度的影响不会很大,但相对于较短的设计,增幅可能达到40%以上。

  在历史上,简单的有序核心的思想一度被抛弃,因为有明显的另一个选择:无序构架。

  四、无序构架

  与有序构架相对的是无序构架。无序构架还是按照程序最初的顺序编译指令,并且仍然按顺序撤销指令,但实际的流程/指令的执行可能是无序完成的。

  让我们来看看所有这些意味着什么吧。如果CPU改变了发送给它的代码的意图,那是毫无用处的。坦白地说,如果双击文件变成执行一串格式化命令的话,那CPU就没有价值了。虽然那是一个极端的例子,但为了确保这样的事情不会发生,CPU必须遵循两个准则:

  指令必须按照程序最初的顺序进行编译(也就是通过CPU解释来找出它们正要求它做什么)

  指令必须按照程序最初的顺序撤销(也就是每个操作的结果必须按照与它被发送到CPU的相同顺序被写入内存/磁盘)

  有序和无序构架两者都遵循这两个准则 - 在这两个阶段之间无序构架所做的处理不同。我们前面提到了有序构架不能对待执行指令重新排序。假如我们拥有一个有序CPU,它有一个加法器和一个载入/存储单元,它被要求执行下列代码(为了简单,我们在这里不讨论带有网络的情况):

  LD R10,R11

  ADD R5,R10,R10

  ADD R9,R9,#1

  ...

  在第一条指令中,我们从存储在R11中的内存地址中载入数据到R10。然后,我们对刚从内存中取出的值做自加运算,并把结果存储在R5中。第三行,也是最后一行对存储在R9中的值赋予1的增量,并保存到R9中。快速浏览一下代码就发现,第二行不能在第一行之前执行。那样做将改变代码的目的(如果想要对某个值做自加运算,首先需要确保有这个值)。不过第三行是完全独立于第一跟第二行的。

  对于有序微处理器,如果第一行要载入的数据就包含在缓存中的话,那么该指令将花费大约1 - 30个时钟周期来完成(根据构架和它所处的缓存级数而有所不同)。第二行在执行前将不得不等足那1 - 30个周期,然后到它执行了以后才轮到第三行。如果被请求的数据不是存储在缓存中的话(或许我们是第一次用到这个值,而我们又没有用到内存中存储在它附近的值),那我们有问题了。出乎意料地,第一行不是用大约1 - 30个周期来完成;现在它将用200个以上的时钟周期来完成。对于第二行倒没什么影响,因为它无论如何都要等第一行完成了才能执行,但对于第三行来说,它可以很容易地在CPU正等着从内存中取出数据的时候执行。跟在第三行后面的任何独立指令也受到缓存失败的影响。

  然而,至于无序微处理器,缓存失败的情况就大相廷径了。代码仍然按照顺序解码,意味着它以跟有序CPU相同的顺序获得指令1,2及3,但这时候我们有了在第一和第二行之前执行第三行的能力,而不是空等着第一行完成。如果发生缓存失败,这就给了无序微处理器一个相当大的性能优势,因为它不是在那等着浪费时钟周期而什么都不干。那么,无序CPU是怎么工作的呢?