CS152 · 第 5 课

Pipelining II:真实流水线与 CDC 6600 的历史伏笔

这一课把第 4 课的抽象流水线原理落到两个真实案例上:MIPS R4000 的真实 8 级流水线(含真实的 load 延迟与分支延迟数字),以及 1964 年的 CDC 6600——它不仅是公认的第一台超级计算机,其 Scoreboard 机制更是乱序执行(out-of-order execution)的历史起点,直接埋下了第 12、13 课的伏笔。

授课:Sophia Shao 预计用时:55 分钟 难度:中等

回顾第 4 课

本课先快速重温精确异常在流水线中的提交点机制(与第 4 课内容一致,此处不再重复展开),然后进入两个具体案例。


真实案例:MIPS R4000 的 8 级流水线

R4000 把经典 5 级流水线进一步拆分成 8 级:IF(取指)→ IS(指令第二拍)→ RF(寄存器读取+标签检查)→ EX(执行)→ DF(数据第一拍)→ DS(数据第二拍)→ TC(标签检查)→ WB(写回)。提交点位于 TC 之后——因为要先确认数据缓存命中与否,才能真正把值写入寄存器。这里有一个精巧的设计:直接映射(direct-mapped)指令缓存允许在标签检查真正完成之前,就先把取到的指令内容提供给流水线使用(标签检查滞后到 RF 阶段才做);数据缓存同理,也允许在标签检查完成前就先使用取到的数据。

Load-use 延迟(重新核对):由于数据在 DS 阶段结束时就可以被旁路使用,R4000 的 load-use 延迟只有 1 个周期(即跟在 load 后面的指令要等 1 拍才能使用 load 的结果)。如果 TC 阶段的标签检查最终显示缺失(miss),流水线会退回一拍,等正确的数据真正可用后再继续——这是"先乐观使用、检测到错误再回退"的推测式设计,与第 4 课"异常预测"的思路一脉相承。

分支延迟(重新核对):因为分支条件在 EX 阶段(第 4 级)才被计算出来,所以 R4000 的基本分支延迟是 3 个周期——分支指令之后已经被取出/译码的 3 条指令,如果分支实际跳转,都必须被丢弃。


编译器如何帮流水线规避冒险:指令调度与循环展开

以简单的向量加法 A[i] = B[i] + C[i] 为例:

loop: fld  f0, 0(x2)     // x2 指向 B
      fld  f1, 0(x3)     // x3 指向 C
      fadd.d f2, f0, f1
      fsd  f2, 0(x1)     // x1 指向 A
      addi x1, x1, 8
      addi x2, x2, 8
      addi x3, x3, 8
      bne  x1, x4, loop

Load 和浮点运算的长延迟限制了单次循环迭代内部能挖掘出的并行度。一个简单的优化是指令重排(scheduling):把两个地址自增指令挪到 load 和 fadd.d 之间,让原本因为等待 load 结果而空闲的周期被有用的地址计算填满,同时把 store 的偏移量相应调整为负值(因为 addi x1,x1,8 被提前了)。

更进一步的优化是循环展开(loop unrolling):把循环体复制多份(例如展开成处理两个元素),暴露出更多独立的并行指令,同时减少循环分支本身的动态执行次数。代价是:展开程度受限于可用的架构寄存器数量;展开会增加静态指令数量,从而增大指令缓存的占用;编译器需要更复杂的指针分析才能生成正确的展开代码。

历史侧写(CS252A 内容):解耦访存/执行(decoupled access/execute)是循环展开之外的另一种思路:把控制流和访存地址计算,与真正的数据计算解耦——因为地址和控制流的计算根本不依赖于数据计算的结果,所以可以提前算好,数据计算则可以延后进行。这预示了后续课程会讲到的乱序执行机制。


延迟、带宽、占用率:三个容易混淆的术语

对同一个功能部件而言:占用率可以远小于延迟(例如全流水化的乘法器,每周期都能接收新操作,占用率只有 1 个周期,延迟却可能是好几个周期);占用率也可以大于延迟(例如未流水化、且启动开销很大的部件);带宽可以大于 1/延迟(多个并行的同类功能部件);带宽也可以小于 1/延迟(只有一个未流水化的部件时)。


历史案例:CDC 6600(1964)——被公认的第一台超级计算机

"超级计算机"并没有单一严格定义,课程给出的几种戏谑说法包括:"给定任务下世界最快的机器"、"把计算瓶颈问题转化成 I/O 瓶颈问题的装置"、"任何造价 3000 万美元以上的机器"、"任何 Seymour Cray 设计的机器"。CDC 6600(Seymour Cray 设计,1964 年)被公认为第一台超级计算机。

硬件规格(原始数字):

真实历史小插曲:1963 年 8 月,IBM CEO Thomas Watson Jr. 在一份内部备忘录中写道:CDC 的 6600 系统开发团队"只有 34 个人,包括门卫在内;其中 14 名工程师、4 名程序员……对比我们庞大的研发投入,我实在不明白,为什么我们会把'世界最强计算机'的头衔拱手让给别人。"Seymour Cray 的回应是:"看起来 Watson 先生已经自己回答了自己的问题。"——这是计算机架构史上最著名的"小团队精干设计打败大公司资源堆砌"的轶事之一。


CDC 6600 的 ISA:为高性能实现而设计

CDC 6600 是一台load/store 架构的机器,把三种寄存器严格分开,各自有专用指令来操作:

所有算术/逻辑指令都是纯寄存器-寄存器格式(15 位:6 位 opcode + 3 位 i + 3 位 j + 3 位 k,Ri ← Rj op Rk);只有 load 和 store 指令才会访问内存(30 位:6 位 opcode + 3 位 i + 3 位 j + 18 位偏移量,Ri ← M[Rj + disp])。一个巧妙的设计是:触碰地址寄存器 A1–A5 会自动触发一次 load,触碰 A6–A7 会自动触发一次 store——这对向量化的访存操作特别有用,因为地址的计算(设置 A 寄存器)本身就隐式地发起了内存操作,天然地把"算地址"和"用数据"这两件事解耦开来。

这套 ISA 设计的每一个决定,都是为了简化高性能实现:


CDC 6600 Scoreboard:乱序执行的历史起点

为了让 10 个功能单元被尽量充分利用,CDC 6600 引入了 Scoreboard 机制来动态调度指令,规则是:

这正是本课程会在乱序执行单元(第 12、13 课)反复回来讨论的起点:Scoreboard 允许指令乱序执行(打破了"指令必须严格按程序顺序执行"这一假设),但仍然靠"派发时检查 WAW""执行前检查 RAW""写回前检查 WAR"这三条规则,在没有寄存器重命名的情况下正确地处理所有三类数据冒险。这比"随便乱序执行"复杂得多,也正是现代乱序处理器的历史源头。


承前启后:更复杂流水线控制中的新问题

一旦引入多个延迟不同的功能单元(整数 ALU、浮点加法器、浮点乘法器、浮点除法器等),经典 5 级流水线的简单假设就会被打破,浮现出新的问题(这些问题正是第 11 课"Complex Pipelines"的开场):

一种简单的应对思路("更复杂的顺序流水线")是:人为拉长所有短延迟操作的写回时机,让所有路径到达写回阶段的延迟保持一致,从而保证写回端口永不过载、且异常仍然可以在统一的提交点按顺序处理——代价是遇到长延迟操作(如除法、缓存缺失)时,整条流水线都要停顿。这个设计选择、以及如何在不牺牲性能的前提下避免这种停顿,正是后续课程要解决的核心问题。


常见误区


知识检查

问题 1:MIPS R4000 的基本分支延迟是 3 个周期,这个数字是怎么来的?

问题 2:CDC 6600 的 ISA 设计中,为什么"触碰地址寄存器 A1-A5"会有特殊含义?

问题 3:CDC 6600 的 Scoreboard 机制,在计算机体系结构历史上最重要的意义是什么?


本课要点

R4000 真实数字

8 级流水线,load-use 延迟 1 拍,分支延迟 3 拍

编译器规避冒险

指令重排、循环展开、解耦访存/执行

CDC 6600

1964 年第一台超级计算机,硬连线控制,load/store ISA

Scoreboard

乱序执行的历史起点,直接预示第 12、13 课


参考资料与延伸阅读