CPU

X86 CPU的发展

从4004谈起

Posted by Wngzh on January 24, 2019

4004到8080,前X86的架构的CPU

  从1978年开始,处理器发展就进入了一个新的时代,从专用处理芯片到通用微处理器时代。在1971年,Intel设计了世界上第一款微处理器,这块微处理器是为Busicom公司生产的芯片,当时Busicom为了生产计算器,向Intel定制了一套用于计算的芯片,12个,但是由于Intel工程师的建议,改为生产一套四个的芯片,名为MCS-4,这套系统有一个中央处理单元CPU-4004,一个ROM,以及一个RAM还有一个输入输出的控制芯片。通过对专利的回购,Intel生产出了世界上第一套可编程通用处理器。

  4004仅仅能够寻址640B的空间,而且只能做加减乘除四种4bit的运算,因此在发布之后,业界反映平淡,直到8008的发布。

  8008是世界上第一款八位处理器,当时与Intel合作的公司是CTC,他们也想设计想4004的Busicom那样的使用集成电路片的计算机产品。这个直接造就了第一台真正意义上的微型计算机。8008可能是第一次开始有指令集设计的CPU,当时的指令集设计是由CTC公司完成的,硬件电路设计则是由IBM和Intel的工程师共同设计,这个指令集的汇编代码开启了向后兼容(backwards compatibility,新版本可以兼容老版产品),8008的指令集不仅仅可以在后续产品8080中对应,在直到今天无处不在的X86架构也可以对应实现。

  8080可能是世界上第一款开始普及的CPU。8080和8008相比,名字来源可能是算力是8008的十倍。从这里开始,CPU工艺从10微米进化到了6微米,意味着越来越多的晶体管开始被集成到了芯片中,同时PMOS更换为了NMOS。虽然8080是一款8bit的CPU,但是已经具备了对16位数据进行操作的能力。

8086到80386,X86诞生

  接下来就是40年经久不衰的X86架构开端8080了。X86就是得名于8086,8080的成功使得8086完全兼容了8080的汇编语言。8086作为第一款16位处理器,在1978年,支持了1M的内存空间寻址。同时还有数学处理器,用于浮点运算的8087。8086拥有AX, BX, CX, DX, SS, DS, CS, ES, IP, SI, DI, SP, BP这样的16位寄存器。此时的8086作为一款CPU产品,能够做的事情还很有限,诸如浮点运算、地址锁存(8282)、数据收发(8286)这样的工作还需要外部芯片来完成,根据外部链接芯片的多少,还分为最大和最小模式。仅仅拥有40 根管脚,与现代的CPU不可同日而语。

  到了80386世代,是xxx86最为辉煌的时刻,如今在Linux中区分64位或者32位操作系统就用的i386代指Intel的80386的32位技术。80386拥有32位的寄存器和32位的地址线,32位的寄存器能够实现存储32bit的偏移量,编程时实现对多达4G的内存空间进行寻址。32位相对于16位来说,处理能力是提高了一倍,原本只能最多单次进行16个二进制的操作,现在能够对32bit进行运算。这对于CPU来说不仅从内存使用上到数据运算上来说,都是十分简单粗暴的性能提升方式。

80486:ILP

  到了486,486中开始引入了流水线(pipeline),从流水线开始,使用这种方法来重叠指令执行的方法被称为指令级并行(ILP)。在8086中,指令执行过程包括取指令,指令译码,指令执行,回写这样的过程,在指令执行的过程中,CPU其他部分处于等待状态不能进行其他操作,执行完成并回写后,才能够执行下一条操作。对于486来说,当进行完第一条指令预取后,马上进行第二条指令预取,此时第一条指令已经进入了指令解码阶段;第三条指令读取后,第二条进入指令解码,第一条进入了执行阶段…实际使用中,通过对流水线的更加细致的划分,可以使得每一步的操作变得简单,增加流水线长度,更容易达到更高的主频。这样原本需要多个指令周期才能够完成的指令,在理想情况下就能实现平均一个周期完成一条指令。最早流水线pipeline概念应用是在RISC处理器中,在MIPS、摩托罗拉88000中就使用这样的设计。但是往往超长的流水线会出现一些问题,在Pentium4 Prescott使用了31级的流水线设计,在2003年就实现了3.8GHz的主频,但是性能相当之差,最后CEO出面下跪道歉,主要原因是在流水线级数增多的情况下,如果执行出错,将会付出很高的代价去修正错误。这里的出错并不是指计算错误,而是分支预测失误导致的指令回溯,流水线越长,付出的时钟周期浪费就越大,例如在jmp指令中,每次出现无条件跳转,往往就需要对流水线清空,重新读取指令队列。另外,流水线冒险是限制流水线效率的问题,流水线并不能够达到完全填满,会出现气泡(bubble),冒险分为这样几类,结构冒险,来自硬件的限制,硬件不足以支持这样几个同时的行为;数据冒险,指令之间的数据使用有依赖关系,执行先后顺序的不同会导致结果不同;控制冒险,对于某些控制逻辑来说,数据执行的改变,例如计数器,会导致执行的错误。为了避免冒险发生,流水线中就必须等待,不再提取新指令执行,就出现了Bubble,气泡,的情况。对于一些数据冒险,可以在每一级流水线之间使用锁存器来保存执行结果,然后使用一种转发的方式,将后级产生的结果通过旁路来送回前级需要的地方,相当于使用了执行结束的结果,减少等待周期,因此另外一个流水线过长带来的问题是,大量时间消耗在锁存器读写上,使得超流水线带来的频率提升变得不划算。

Pentium与超流水线、SIMD指令集

  486的后继者Pentium,这时候的Pentium已经有了Cache和集成在CPU内部的浮点运算单元,配备了新的指令集MMX,并且使用了超标量技术。超标量同时使用多条流水线实现指令上的并行,这个多条流水线能够并行的指令数目又被称为是发射数对于现代CPU来说,APPLE公司基于ARM架构的A8,能够实现6发射。在两条流水线同时运行与流水线同时运行多条指令有些类似,都涉及到了指令相关联带来的结果受执行顺序影响这样的问题。但是与流水线的冒险相比,因为超标量的实现是基于多套硬件结构,并不会出现硬件资源冲突的问题。为了避免两条流水线上的指令出现相关联对运行结果的影响,对于超标量的判断,不仅要判断到数据和控制上的冒险操作,还要明确判断相关性,其中一种相关被称为是名称相关,是并行执行中两条指令对相同的寄存器进行读写造成的(但是数据上实际上不相关,也就是说在这两条指令间名称相关的部分没有数据流动),这里只需要使用不同的寄存器就可以避免这种情况,这样在软件上使用的办法是在编译器层面上的优化,对硬件层面,使用一种寄存器重命名的方法,也能够达到对使用的寄存器避免名称冲突的效果。在超标量两条流水线运行中,必须通过停顿等方式的调度,来避免相关带来的影响。但是相对于RISC结构,RISC指令长度相同,执行时间也相同,判断执行的每一部分是否相关比X86容易的多,X86指令不等长、指令执行时间也不同,给并行执行判断相关性带来了极大的困难。对此Intel将执行中的X86指令分解为micro-ops(中文名是微指令或者是微程序),这是一种RISC风格的指令,这样就能实现指令级的并行。在Pentium中,流水线分为U、V两条。执行PF D1 D2 EX WB五个步骤,在D1的译码阶段如果取出的两条指令配对成功,就能够在U、V两条流水线上执行,如果不能够并行,则仅在U流水线执行。遇到指令受阻时,如果发生在U流水线,那么两条流水线全部退出,如果发生在V,则U流水线运行不受影响。

  同时Pentium开始配备了新的向量指令集MMX,在相当早的微架构P5中,Pentium就已经开始使用这种技术,其中96年发布的Pentium MMX就是以这种指令集命名,MMX的意思可能是Matrix Math eXtensions,用途主要是处理多数据的操作,MMX的后继者有SSE(Streaming SIMD Extensions),AVX(Advanced Vector Extensions),最先进的Core i9系列的CPU中,就使用了AVX-512指令集,能够使用多达512bit的寄存器进行运算。以上的这些指令被分类为SIMD,单指令流多数据流,能够同时将多个数据用同样的指令操作。这种操作通常被用在多媒体处理上,在这种情况下,常常要对大量的数据(视频、图片等)进行成组的重复操作,因此MMX又被称作是多媒体扩展指令,能够极大的加快浮点运算的速度。这样工作又被称为是数据级并行,例如一个单一的SIMD指令,将64个数相加,只需要将64个数发送到64个ALU,就可以在一个时钟周期内得到64个数据之和,SIMD这样的实现需要硬件有很多并行工作的ALU,例如在SSE中使用的256bit寄存器,可以看作是8个32bit的浮点数,也可以看做是4个64bit的双精度浮点数,寄存器的位长越大,浮点运算的并行度就越高。这项技术现在被广泛运用在图像处理单元中,CPU和GPU是一个异构多处理体系实例,GPU主要完成的是3D几何的绘制和像素基元的着色和渲染,这些对象用一个32位浮点数来表示,因为图像数据按组4个一起出现,过去常使用SIMD来同时处理,但是现在往往考虑到通用性,不再使用,而是靠堆砌大量的多处理器,并行运算,来实现同时处理大量数据。相对于单指令单数据体系来说,SIMD节约了大量的取指令和指令译码的时间,二是如果使用SIMD指令编程,隐含指明了数据之间是不相关的,这样就免去了数据相关检查的开销,最后,这样的程序往往是连续的数据存取,元素的地址连续,读写速度能够加快,相当只进行了一次延迟开销。使用这种方法,在最新的Core处理器中,能够获得与上一代GPU效率相近的运算性能。除了上面提到的内容,这一代Pentium还引入了片上Cache,在此不再叙述,具体在另一片文章中会较为详细的给出。

流水线优化:Pentium Pro与乱序执行分支预测

  在95年发布的Pentium Pro中,加入了更加高级的指令级并行技术,乱序执行(Out-of-order execution)和分支预测(Speculative execution)。这两种方法都是用来提高流水线的指令级并行效率的方法。我们可以通过预测指令的执行结果,例如预测一个分支的运行结果,CPU可以提前执行分支后的其他指令。但是如果猜错,就必须取消原来的预测结果,并进行回滚。分支预测需要大量的额外开销,来决定是否应该使用预测和检查结果是否正确以及回滚机制,如果使用不慎,则极有可能会降低性能。分支预测的技术有相关分支预测和竞赛分支预测等。分支预测就是使用分支最近的行为来预测下一次行为。如果只使用单个分支最近的行为来预测下一次行为,被称为是两位预测器;如果考虑其他分支的行为来进行预测,被称为是相关预测。一个(m,n)的分支预测器,使用最近m个分支的行为从2^m个分支预测器中进行选择,其中每个预测器都是单分支的n位预测器,这种方式进行预测,很容易可以想到预测的成功率会比2位的方案更高。而且如果使用m位移存器中,每一位记录是否进行了分支转移,将分支地址的低位与m位串联在一起,就可以实现对于分支预测缓冲区的寻址。计算结果表明,使用相关预测器使用较少的项数,就可以实现优于无限项的简单两位预测器。另外一种是竞赛预测器,这种预测器基于局部信息和全局信息,能够为特定的分支选择正确的预测器,在AMD和Intel的Core处理器中均有使用。竞赛预测器使用一个两位饱和预测器,根据哪种预测器(全局或者局部)在最近的预测中最为有效,来选择使用哪一个预测器来预测。相对于相关预测器,竞赛预测器效果会更好一些,理想的错误率在稍低于3%的水平。乱序执行指的是一种动态调度流水线的方式。对于流水线的数据冒险和相关等情况,静态调度会提取并发射一条指令,遇到数据相关,则会停顿流水线直到相关消除,对于动态流水线调度处理器,CPU在不改变程序原有的数据流前提下,对指令的执行顺序进行改变,以达到减少停顿的目的。在这种CPU中,流水线被划分成三个单元,取指令与发射单元、多个功能单元和一个提交单元。第一个单元取指令并进行译码,然后交到相应的功能单元执行,每个功能单元有自己的保留站(缓冲区),用来保存操作数和指令;当得到结果后,结果发送到提交单元的缓冲区,确定结果没有危险后,提交单元将结果回写寄存器或者存储器。在发射指令时,具体考虑这样的过程:将指令复制到功能单元的保留站,若操作数立即可用,则马上执行,原来用于保存操作数的寄存器可以被覆盖写入;如果操作数不再需要的地方,那么硬件帮助定位操作数结果产生的地方,将结果直接送到保留站中。最后提交单元按照程序顺序结果将缓冲区中的内容写回寄存器和存储器。这种顺序提交是现行CPU的通用做法。这样就相当于实现了对于程序数据的分析,在不违背程序原有数据流的前提下完成了乱序执行。具体的算法有Tomasulo算法等,在此不进行详述。

性能发展

  关于x86CPU的性能,早年的资料已经很难找到具体的每秒运算次数,因此来通过一些硬件指标来看性能发展。

4位处理器

4004-1971年

时钟频率740KHz,0.07MIPS(每秒百万次指令数),2.3K晶体管

8位处理器

8080-1974年

2-3MHz,0.37MIPS,4.5K晶体管

16位处理器

8086-1978年

5-10MHz,0.33-0.75MIPS,最高1M的内存,29K晶体管

80286

6-12.5MHz,0.9-2.66MIPS,最高16MB内存,134K晶体管,性能是8086的3-6倍

32位处理器

80386-1985年

16-33MHz,5-9.9MIPS,最高4GB内存275K晶体管

80486-1989年

25-50MHz,20-41MIPS,最高4GB内存和1200K晶体管,带有8KB的Cache

奔腾P5架构-1993年

60-300MHz,100-270MIPS

P6架构-1995年

  一直到2006年的Core,都有产品使用的P6架构,最初的一直到2006年,的Core,能够达到2.33GHz,在最初的Pentium Pro中,能够达到150-200MHz的主频,这是第一次Intel在CPU上设计超标量和乱序执行以及分支预测。同时代表着CPU的使用越来越复杂,之前简单的MIPS指标已经不能完全评估CPU的性能水平。

NetBurst-2001年

  从Pentium4的1.2GHz到Pentium EE的3.7GHz,使用了超长的流水线,能够很容易的提升频率,但是因为流水线过长,实际性能并不好。

Core微架构 - 2006年、Nehalem - 2008年、Sandy Bridge - 2009年、Ivy Bridge - 2010年、Haswell - 2011年、Broadwell-2015年、Skylake - 2016年、Kabylake - 2017年、Ice lake – 2019年

  这是近年来使用的CPU微架构,相对于前一代,每一代都有20%-40%的性能提升和一定的能耗优化,相对于更早的微架构,Core的性能同频性能更好,同时集成了GPGPU来分担CPU的工作,诸如AVX之类的高级指令集的加入使得发挥出完整的性能,编程上越来越复杂。

  从1971年开始,到现在,Intel尽力使CPU的发展遵从摩尔定律,根据描述,每隔18个月,CPU的晶体管数目就会增加一倍,同时性能提升一倍,同性能的计算机就可以便宜一半。

  但是摩尔定律从2010年开始逐渐遇到了瓶颈,Intel在2010年指定的Tick-Tock(制程更新-架构更新)战略也逐渐变成了Process, Architecture, Optimization,即制程、架构、优化。制程提升开始受到物理法则的限制,按照现在的技术,在未来几年很可能会遇到物理上的制程上限,未来的CPU提升可能更多的在能耗比和微架构优化,以及外部设备的协同上。

结语

  CPU的性能在不断提升,设计也越来越复杂,现在的一个X86的复杂功能核心(Sandy Bridge)相比于RISC的UltraSPARC,同样拥有10亿的晶体管,但是前者只有4核心,后者却拥有16个核心,这是两种不同的设计思路,是要更强大的核心还是更多的核心?前文中提到的种种技术,使得现代的CPU中,仅有一小部分电路是负责运算,大量的外围电路为逻辑控制、指令处理服务。未来进一步提升性能,是设计更加强大的核心,还是用更多简单的核心分担任务,亦或是使用专用处理器来逐步分担CPU的功能,还有待观察。

参考文献

[1]John L. Hnnessy, David A. Patterson. Computer Architecture:A Quantitative Approach,Fifth Edition [M]. Morgan Kaufmann,2011.

[2]John L. Hnnessy, David A. Patterson. Computer Organization and Design:The Hardware/Software Interface,Fourth Edition [M]. Morgan Kaufmann,2004.

[3] 1965: “Moore’s Law” Predicts the Future of Integrated Circuits, Computer History Museum [EB/OL]. http://www.computerhistory.org/siliconengine/moores-law-predicts-the-future-of-integrated-circuits/

[4] x86, Wikipedia, the free encyclopedia [EB/OL]. https://en.wikipedia.org/wiki/X86

[5]Intel Tick-Tock, 维基百科,自由的百科全书 [EB/OL]. https://zh.wikipedia.org/wiki/Intel_Tick-Tock

[6] List of Intel microprocessors, Wikipedia, the free encyclopedia [EB/OL]. https://en.wikipedia.org/wiki/List_of_Intel_microprocessors