精简指令系统计算机(RISC)

RISC 的口号是”精简指令”,但它精简的不是指令的条数,而是每条指令的功能。这个区别是本节一切结论的起点:

RISC 的核心主张:只保留最常用的简单指令,让每条指令都能在一个流水段内完成同样的事;复杂功能交给编译器用多条简单指令合成。

换句话说,它把 CISC 花在”让硬件理解复杂语义”上的预算,全部挪去买流水线、寄存器和时钟频率。 4.4.2 要讲清的就是:这笔钱为什么值得挪。

机制

典型特征

维度RISC 的做法换来什么
指令长度定长PC 增量恒定、取指与译码可并行
指令格式少数几种,字段位置固定译码一级完成,可提前读寄存器
寻址方式少(常只有 3~5 种)有效地址计算路径单一且短
访存只有 load / store访存只在固定的一个流水段发生
指令周期等长,流水下 CPI 接近 1流水段可以整齐切分
通用寄存器多(≥32 个)减少访存次数
控制器典型搭配硬布线译码延迟最小
优化重心编译器(寄存器分配、指令调度)硬件保持简单

这张表的每一行,右边那一列都指向同一个词:流水线。 RISC 的全部设计选择都是为了让流水线能跑满,这是理解它的唯一钥匙。

Load/Store 结构:为什么它是 RISC 最核心的一条

规定”只有 load/store 能访存”,看上去只是个限制,实际上它决定了流水线能不能整齐切分。

看两种做法在流水线上的差别。假设五段流水:取指 IF → 译码 ID → 执行 EX → 访存 MEM → 写回 WB。

RISC 的 add r1, r2, r3:

IF → ID → EX → (MEM 空过) → WB

CISC 的 add [ebx+8], eax(把主存单元加上 eax 再写回主存):

IF → ID → 算地址 → 读主存 → 加法 → 写主存

问题不在于它慢,而在于它的形状和别的指令不一样:

RISCCISC
每条指令的访存次数0 次或 1 次,且位置固定0、1、2 次都有可能
每条指令经过的阶段完全相同各不相同
流水段能否固定划分✅❌ 有的指令要在流水线中段访存两次

流水线的前提是”每条指令经过同样多、同样长的阶段”。 一旦有指令需要在中途访存两次,要么整条流水线为它加段(所有指令都变慢),要么让它阻塞流水线(5.5(本地资料) 的结构冒险)。Load/Store 结构就是从 ISA 层面直接禁止这种情况发生。

所以这条规定的真正含义是:把”访存”这件耗时且长度不定的事,隔离到专门的两条指令里,其余所有指令的行为都变得整齐可预测。

定长指令:把好处兑现到取指段

4.1.3 列过三条收益,在流水线上逐条兑现:

定长带来的在流水线上是什么
PC 增量是常数取指段不必等译码结果,可以一直往前预取
操作码位置固定译码是一级组合逻辑,ID 段延迟固定
寄存器字段位置固定ID 段可以在译码没出结果时就先读寄存器(读错了不用即可)→ 译码和取操作数合成一拍

第三条最值钱,它直接把流水线少掉一整段。这就是 4.1.4 那条主线的终点——变长指令省下的存储空间,在这里以流水段的形式加倍还了回去。

寄存器多:把访存次数按下去

RISC 通常有 32 个以上通用寄存器,这不是奢侈,是 Load/Store 结构的必要配套:既然只有 load/store 能访存,那中间结果就必须放得下。

回到 4.1.2 的访存次数表:操作数在寄存器里,那一次访存直接消失。 一段计算密集的代码,寄存器够多就能整段只在头尾各访存一次。在 3.1.2 那条”延迟差几个数量级”的背景下,这是最直接的性能来源。

寄存器多还有一个副作用:寄存器编号字段要变长(32 个寄存器要 5 位)。三地址指令就要 15 位放寄存器号——定长 32 位指令字正好装得下,这几个数字是互相配合设计出来的。

编译器接过了担子

RISC 把复杂度从硬件推给了编译器,所以 RISC 的成立前提是编译器足够好——这正是 CISC 那三条历史约束里消失得最彻底的一条。编译器要额外承担:

  • 寄存器分配:把哪些变量常驻寄存器(图着色算法)
  • 指令调度:重排指令顺序以填满流水线空档、避开数据相关(5.5(本地资料))
  • 复杂操作的合成:一条 CISC 指令的活,拆成几条 RISC 指令并安排好顺序

“RISC 把优化重心从硬件移到编译器”这句话,具体就是指这三件事。

边界

RISC 精简的是”每条指令的功能”,不是”指令的条数”。 RISC-V 加上各种扩展后指令数量并不少,但它仍然是 RISC,因为每条指令依然简单、依然只有 load/store 访存。判断题若说”RISC 的指令条数一定比 CISC 少”,错。

“RISC 每条指令一个时钟周期”要按流水线口径理解。 一条指令从取指到写回仍要经过 5 个时钟周期(延迟),但流水线满载时平均每个周期完成一条(吞吐)。说的是 CPI 接近 1,不是延迟等于 1 个周期。 这正是 3.1.2 那条”延迟和吞吐是两个量”的主线在第 4 章的又一次出现。

“RISC 用硬布线控制器”是典型搭配,不是定义。 和 CISC 那条对称:定义在指令集,不在控制器。不过这个搭配的因果关系比 CISC 那边更紧——指令简单且格式统一,硬布线的组合逻辑才做得出来。

Load/Store 结构不等于”访存少”。 完成同样的工作,RISC 的访存数据量和 CISC 一样多(数据该读还得读),变的是访存被集中到了哪几条指令里。省下来的是那些本可以在寄存器里完成的中间结果的访存,这要靠寄存器多来兑现。

延迟槽是 MIPS 的历史包袱,不是 RISC 的通则。 早期 MIPS 让转移指令后面那条指令无论转不转都执行(把流水线气泡交给编译器填),RISC-V 和 ARM 都没有采用。统考若提到延迟槽,按题目说明处理即可。

RISC 也需要 Cache,而且更需要。 指令条数比 CISC 多、代码密度低,指令 Cache 的压力反而更大——这是 CISC 在 4.4.3 里唯一还站得住的一条优势。

对照速查

特征RISC换来什么
定长指令✅取指与译码可并行
格式统一、字段固定✅ID 段可提前读寄存器
寻址方式少✅地址计算路径短
只有 load/store 访存✅流水段可整齐划分
CPI ≈ 1(流水满载)✅吞吐高
通用寄存器 ≥32✅减少访存
硬布线控制典型搭配译码延迟最小
编译器优化重硬件保持简单
判断对错
RISC 的指令条数一定少于 CISC❌
RISC 精简的是单条指令的功能✅
RISC 每条指令的执行延迟是 1 个周期❌(CPI≈1 是吞吐口径)
RISC 必须用硬布线控制器❌(典型搭配)
Load/Store 结构使总访存数据量减少❌(减少的是中间结果的访存)
RISC 代码密度比 CISC 高❌(低)
定长指令使 ID 段可以提前读寄存器✅
延迟槽是所有 RISC 的共同特征❌(MIPS 的历史选择)

考点

  • RISC 精简的是每条指令的功能,不是指令条数
  • 特征:定长、格式统一、寻址方式少、Load/Store、CPI≈1、寄存器多、典型配硬布线、编译器优化重
  • Load/Store 结构的意义是让每条指令经过相同的流水段,从 ISA 层禁止”中途访存两次”
  • 定长指令的最大收益:ID 段可在译码出结果前先读寄存器,省掉一整段
  • 寄存器多是 Load/Store 的必要配套,直接消掉访存次数表里的那几次
  • CPI≈1 是吞吐口径,不是延迟口径
  • 编译器接手三件事:寄存器分配、指令调度、复杂操作合成
  • 硬布线控制是典型搭配不是定义;RISC 代码密度低

链接