流水线技术是提升性能的银弹吗?它通过把一条指令的操作切分成更细的多步,可避免CPU“浪费”。每个细分的流水线步骤都很简单,单个时钟周期时间就可设得更短,变相让CPU主频提升很快。
这一系列优点,引出现代桌面CPU最后大战:Intel Pentium 4 V.S AMD Athlon。 技术上,Intel输得彻底,Pentium 4系列及后续Pentium D系列所使用的NetBurst架构被完全抛弃。但商业层面,Intel却通过远超过AMD财力、原本就更大的市场份额、无所不用的竞争手段,以及最终壮士断腕般放弃整个NetBurst架构,最终依靠新的酷睿品牌战胜AMD。
此后,整个CPU领域竞争焦点,不再是Intel和AMD的桌面CPU。ARM架构通过智能手机普及,后来居上,超越Intel,移动时代CPU变成高通、华为麒麟和三星之间的“三国演义”。
1 “主频战争”带来的超长流水线
不能简单通过CPU主频,就衡量CPU乃至计算机整机性能。不同CPU实际体系架构和实现都不同。同样CPU主频,实际性能可能差别很大。更好的衡量方式是用SPEC这样跑分程序,从多个不同实际应用场景衡量性能。 但跑分对消费者还是太复杂。Pentium 4的CPU面世前,绝大部分消费者并不根据跑分判断CPU性能,通常只看CPU主频。CPU厂商也不停提升主频,当成技术竞赛核心指标。
Intel一向在“主频战争”中保持领先,但1999年,AMD发布基于K7架构的Athlon处理器,综合性能超越当年的Pentium III。2000年,大部分CPU还在500~850MHz,AMD推出第一代Athlon 1000处理器,成为第一款1GHz消费级CPU。2000年前后,AMD的CPU不但性能和主频比Intel的要强,价格还往往只有2/3。Intel在2001年推出新一代NetBurst架构CPU,即Pentium 4和Pentium D。Pentium 4CPU最大特点高主频。2000年的Athlon 1000的主频在当时是最高的,1GHz,Pentium 4设计最高主频10GHz。
为达到10GHz,Intel工程师做个重大错误决策,就是在NetBurst架构上,使用超长流水线,有多长? Pentium 4之前的Pentium III CPU,流水线深度11级:一条指令最多拆成11个更小步骤操作,而CPU同时也最多会执行11条指令的不同Stage。今天日常手机ARM的CPU或Intel i7服务器的CPU,流水线深度14级。
20年,技术进步,现代CPU还是增加一些流水线深度。2000年的Pentium 4的流水线深度20级,比Pentium III多一倍,而到代号为Prescott的90纳米工艺处理器Pentium 4,Intel更是把流水线深度增加到31级。 增加流水线深度,在同主频下,其实是降低CPU的性能。因一个Pipeline Stage,就需一个时钟周期。把任务拆成31个阶段,就要31个时钟周期才能完成一个任务;而把任务拆分成11个阶段,就只要11个时钟周期完成任务。这种情况下,31个Stage的3GHz主频的CPU,其实和11个Stage的1GHz主频的CPU,性能差不多。因为每个Stage都要有对应Pipeline寄存器的开销,这时,更深的流水线性能可能更差。
流水线技术并不能缩短单条指令的响应时间,但可增加在运行很多条指令时候的吞吐率。因为不同指令,实际执行需要时间不同。如顺序执行这样三条指令:
- 一条整数的加法,200ps
- 一条整数的乘法,300ps
- 一条浮点数的乘法,600ps
若在单指令周期CPU运行,最复杂指令浮点数乘法,就要600ps。那这三条指令,都要600ps。三条指令的执行时间,就要1800ps。若采用6级流水线CPU,每个Pipeline的Stage都只需100ps。则这三个指令的执行过程中:
- 指令1的第一个100ps的Stage结后,第二条指令就开始执行了
- 第二条指令的第一个100ps的Stage结束后,第三条指令就开始执行了
这种情况下,这三条指令顺序执行所需要的总时间800ps。1800ps内,使用流水线的CPU比单指令周期的CPU就可以多执行一倍以上的指令数。
虽然每条指令从开始到结束拿到结果的时间并没有变化,即响应时间没变。但同样时间内,完成指令数增多,即吞吐率上升。
这不是很好么?Intel CPU支持2000多条指令:
- 有些指令简单,执行很快,如无条件跳转指令,不需通过ALU,只更新PC寄存器内容
- 有些指令复杂,如浮点数运算,需进行指数位比较、对齐,然后对有效位进行移位,然后再计算
两者执行时间相差二三十倍也很正常。既然这样,Pentium 4的超长流水线看起来很合理,为何Pentium 4最终成为Intel在技术架构层面的大失败:
2 功耗问题
提升流水线深度,须和提升CPU主频同时进行。因为单个Pipeline Stage能执行的功能变简单了,即单个时钟周期内能够完成的事情少了。所以,只有提升时钟周期,CPU在指令的响应时间这个指标上才能保持和原来相同性能。
由于流水线深度增加,需电路数量变多,即晶体管多了。主频的提升和晶体管数量的增加都使CPU功耗变大。导致Pentium 4在整个生命周期严重耗电和散热。 Pentium 4是在2000~2004年作为Intel 主打CPU出现在市场。正是笔记本电脑市场快速发展的时间,功耗和散热比起台式机是更严重问题。
3 流水线技术带来的性能提升,是理想情况
实际程序执行中,并不一定能够做得到。刚才举的三条指令。若这三条指令有如下代码,会咋样?
代码语言:javascript复制int a = 10 5; // 指令1
int b = a * 2; // 指令2
float c = b * 1.0f; // 指令3
指令2不能在指令1的第一个Stage执行完成后进行,因为指令2依赖指令1的计算结果,指令3也要依赖指令2计算结果。 即使采用流水线技术,这三条指令执行完成的时间,也是 200 300 600 = 1100 ps,而不是 800ps。 若指令1和2都是浮点数运算,需600ps。那这个依赖关系会导致我们需要的时间变成1800ps,和单指令周期CPU时间一样。
这就是
4 冒险(Hazard)问题
这列举在数据层面的依赖,即数据冒险,还有结构冒险、控制冒险等其他依赖问题。
对这些冒险问题,也有在乱序执行、分支预测等相应解决方案。 但流水线越长,冒险问题越难解决。因为同一时间同时在运行的指令太多。若只有3级流水线,可把后面没有依赖关系的指令放到前面执行。即乱序执行技术:
代码语言:javascript复制int a = 10 5; // 指令1
int b = a * 2; // 指令2
float c = b * 1.0f; // 指令3
int x = 10 5; // 指令4
int y = a * 2; // 指令5
float z = b * 1.0f; // 指令6
int o = 10 5; // 指令7
int p = a * 2; // 指令8
float q = b * 1.0f; // 指令9
可不先执行1、2、3指令,而是在流水线里,先执行1、4、7,这三条指令无依赖关系。 再执行2、5、8及3、6、9。这又能够充分利用CPU计算能力。
但若有20级流水线,要确保这20条指令之间无依赖关系。挑战变大很多。毕竟我们平时撰写程序,通常前后的代码都有一定依赖关系,几十条没有依赖关系的指令可不好找。所以超长流水线执行效率发而会降低。
5 总结
流水线技术和其他技术一样,需Trade-Off。一个合理的流水线深度,会提升CPU执行计算机指令的吞吐率。一般用IPC(Instruction Per Cycle)来衡量CPU执行指令的效率。
IPC是CPI(Cycle Per Instruction)的倒数。即IPC = 3对应CPI = 0.33。Pentium 4和Pentium D的IPC都远低于自己上一代的Pentium III以及竞争对手AMD的Athlon CPU。
过深流水线不仅不能提升计算机指令吞吐率,还会加大计算功耗和散热。
而流水线带来吞吐率提升,只是个理想情况下的理论值。在实践的应用过程中,还需要解决指令之间的依赖问题。这个使得我们的流水线,特别是超长的流水线的执行效率变得很低。要想解决好冒险的依赖关系问题,引入乱序执行、分支预测等技术。
参考
- Modern Microprocessors, A 90-Minute Guide