流水线技术是提升性能的银弹吗?它通过把一条指令的操作切分成更细的多步,可避免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变成高通、华为麒麟和三星之间的“三国演义”。
“主频战争”带来的超长流水线
其实并不能简单通过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在技术架构层面的大失败:
功耗问题
提升流水线深度,必须要和提升CPU主频同时进行。因为单个Pipeline Stage能够执行的功能变简单了,也意味单个时钟周期内能够完成的事情少了。
所以,只有提升时钟周期,CPU在指令的响应时间这个指标上才能保持和原来相同的性能。
由于流水线深度增加,需要电路数量变多,即晶体管多了。
主频的提升和晶体管数量的增加都使得CPU功耗变大。导致Pentium 4在整个生命周期里,严重耗电和散热。
Pentium 4是在2000~2004年作为Intel的主打CPU出现在市场上的。这个时间段,正是笔记本电脑市场快速发展的时间。在笔记本电脑上,功耗和散热比起台式机是更严重问题。
流水线技术带来的性能提升,是理想情况
实际程序执行中,并不一定能够做得到。
刚才举的三条指令。若这三条指令,是下面代码,会发生什么情况呢?
inta = 10+ 5; // 指令1intb = a * 2; // 指令2floatc = b * 1.0f; // 指令3
发现,指令2不能在指令1的第一个Stage执行完成后进行。
因为指令2依赖指令1的计算结果。
同样指令3也要依赖指令2计算结果。
这样,即使采用流水线技术,这三条指令执行完成的时间,也是 200 + 300 + 600 = 1100 ps,而不是 800ps。
若指令1和2都是浮点数运算,需要600ps。那这个依赖关系会导致我们需要的时间变成1800ps,和单指令周期CPU时间一样。
这就是冒险(Hazard)问题。这列举在数据层面的依赖,即数据冒险,还有结构冒险、控制冒险等其他依赖问题。
对这些冒险问题,也有在乱序执行、分支预测等相应解决方案。
但流水线越长,冒险问题越难解决。因为同一时间同时在运行的指令太多。若只有3级流水线,可把后面没有依赖关系的指令放到前面执行。这个就是我们乱序执行技术:
inta = 10+ 5; // 指令1intb = a * 2; // 指令2floatc = b * 1.0f; // 指令3intx = 10+ 5; // 指令4inty = a * 2; // 指令5floatz = b * 1.0f; // 指令6into = 10+ 5; // 指令7intp = a * 2; // 指令8floatq = b * 1.0f; // 指令9
可不先执行1、2、3指令,而是在流水线里,先执行1、4、7,这三条指令无依赖关系。
再执行2、5、8及3、6、9。
这又能够充分利用CPU计算能力。
但若有20级流水线,要确保这20条指令之间无依赖关系。挑战变大很多。毕竟我们平时撰写程序,通常前后的代码都有一定依赖关系,几十条没有依赖关系的指令可不好找。
所以超长流水线执行效率发而会降低。