架构演进

RISC与CISC架构演进、指令编码空间博弈与GPU崛起

Posted by CloudingYu on March 16, 2026

一、大模型辅助开发的方法

用大模型辅助 debug 已经成为趋势,但需要正确的方法——不是简单说”给我一个能运行的代码”,而应该给大模型明确的示例(few-shot),告诉它”我希望代码写成什么样子”。

代码基础一定要打好:每一行都要搞清楚,否则后续扩展会越来越痛苦。大模型时代,写代码可以更高效,但用好 prompt 跟大模型沟通很重要。代码是基础,每一行都要搞清楚、搞明白,地基没打好,回头增加功能就完了。


二、RISC 设计哲学的再审视

2.1 为什么需要 RISC:80-20 原则

RISC(精简指令集计算机,Reduced Instruction Set Computer)的核心动机:

  • 对大量程序统计发现,简单指令(load、branch、compare、store 等)使用频率极高,常用的十来条指令占到了代码的 96%
  • 80-20 原则:80% 的指令只占 20% 的使用率。设计 CPU 时应该把精力集中在高频指令上,让它们跑得更快。
  • 与其支持上千条复杂的 CISC复杂指令集计算机,Complex Instruction Set Computer)指令,不如把十几条高频指令做到极致。

2.2 RISC 的核心设计特征

  1. 指令尽量少:指令少,芯片做得很简洁,成本很低。
  2. 寄存器-寄存器操作:除 load/store 之外,其他指令都不直接访问 memory。因为一旦涉及 memory,指令需要多少个 clock 就不确定了。
  3. 大量通用寄存器:采用大量通用寄存器(General Purpose Register, GPR)来减少访存次数。
  4. 组合逻辑控制器:用门电路而非微程序实现控制单元。
  5. 优化的编译系统:力求有效支持高级语言。

2.3 寄存器的数量之辩

  • MIPS 设计了 32 个 GPR(实际 31 个可用,R0 恒为 0)。
  • 以前更少(16 个),甚至更早只有 AX、BX、CX、DX 等少数专用寄存器。
  • 为什么不多来点?不是因为没钱,是有钱也用不起——寄存器的位数必须放在指令编码中,增加寄存器数量会挤占 OP 码或立即数空间。

PC 是个寄存器,能不能跟通用寄存器放一起?至少现在没放,但仔细想好像也没什么特别的。

2.4 RISC-V 的无标志位设计

  • RISC-V 没有 零标志位(Zero Flag, ZF)和 溢出标志位(Overflow Flag, OF)。
  • x86 的做法:硬件做完运算产生标志位,软件判读。
  • MIPS 的做法:硬件直接检测溢出并产生异常。
  • RISC-V 的做法:干脆不产生标志,把结果放那儿,程序员自己判断。

RISC-V 的想法就是”做个超简单的硬件,让软件工程师去处理”。一个除法得在编译器里额外加一段检查代码,老这么做检查确实有一定开销。

2.5 RISC-V 的开源属性与碎片化风险

  • RISC-V 是开源指令集,可以随便修改,没有知识产权(IP)问题。
  • 但修改之后需要单独的编译器,导致碎片化——你做的东西别人用不了。
  • 不像 Linux 可以直接下载使用,为了改而改可能破坏生态。

计算机组成是一个工程师设计的世界,每一个选择都有它的理由。也许后来的人觉得很傻,但那一刻那个理由在现在这个时代已经消失了。


三、x86 指令集的演变:兼容性的战争

3.1 变长指令的包袱

x86 的指令编码随着历史不断膨胀:

  • 8086/80286:1 字节指令
  • 逐渐扩展到 2 字节、3 字节、4 字节……现在最长可达 15 个字节
  • 前面的短指令没法丢掉,只能不断往后加,越做越长

每个历史包袱都像一块砖,越堆越高。

3.2 x86 与 RISC 的兼容性悖论

  • 客户的唯一诉求就是兼容——以前有的代码别给扔掉。
  • x86 不能像 IA-64(Intel 从头设计的新架构,但直接失败了)那样推倒重来,因为”不保留就死了”。

3.3 Intel 的解决方案:翻译层 + 内部 RISC 核

Intel 的关键策略——硬件翻译层

  1. 对外暴露的是 CISC 指令集(兼容旧代码)
  2. 内部把 CISC 指令 translate(翻译) 成 RISC 指令
  3. 内部 RISC 核可以跑得更快(800MHz -> 3GHz 级别)
  4. 软件工程师不用管,硬件帮你搞定

这比让软件工程师做迁移代价小得多——因为每一家软件都要来整一遍,代价巨大。这是思路打开的问题——思路打开了,刹那天地宽。为什么要在硬件工程师这边做?因为它已经到了二进制指令层面,可以做指令调度、指令优化,还保证逻辑不出错。

3.4 为什么”假装用”旧指令行不通

别想着把原来 8086 的指令”假装用一下”——现实很复杂:

  • 所有软件代码非常复杂,针对特定 CPU 做了大量优化
  • 开个 VM(虚拟机)把代码全搬过去,效果很不好
  • x86 到 ARM 的迁移代价巨大,没有专门的人去做全面迁移

四、x86 指令的具体结构

4.1 指令组成

一条 x86 指令可能包含:

  1. 前缀(0 或 1 字节)
  2. 操作码(Opcode)
  3. 寻址方式字段(Mod R/M, SIB)
  4. 位移量(Displacement):1/2/4 字节
  5. 立即数(Immediate):1/2/4 字节

指令从 1 字节到 15 字节不等,处理起来需要在 decode 阶段反复判断”这个指令是什么、哪个格式、怎么处理”。

4.2 寻址方式的丰富性

x86 有极其丰富的寻址方式:

  • 寄存器寻址
  • 立即数寻址
  • 基址 + 变址 + 比例因子 + 位移(Base + Index * Scale + Displacement)

例如 MOV AX, [ESI*8 + 100]:ESI 是变址寄存器,先乘 8,再加 100(基址),再取数据。

这种寻址方式的核心动机是快速访问数组元素

  • 访问 A[I] 时,需要计算 A_base + I * sizeof(element)(通常是 *4 或 *8)
  • 一般情况下需要用多条指令(乘法 + 加法 + 访问),x86 用一条就搞定了

4.3 数组寻址与地址对齐

  • 数组 A[0] 在地址 0x100,A[1] 在哪取决于元素类型:char 跳 1 字节、int 跳 4 字节
  • 计算 A[I] 时:0x100 + I * 4
  • 比例因子(Scale)就是 1248,对应不同元素大小

以后大概率会涉及自己写的数据结构怎么调都不对,就忍不住想看一看在 memory 里面是怎么布局的,访问 memory 的时候发生了什么,一次读了几个字节——而这些寻址方式就决定了访问的效率。


五、MIPS 指令格式分析

5.1 三大指令类型

MIPS 将所有指令分为三种固定格式:

类型 说明 编码构成
R 型 寄存器-寄存器运算 OP(6) + RS(5) + RT(5) + RD(5) + SHAMT(5) + FUNCT(6)
I 型 立即数运算/访存/分支 OP(6) + RS(5) + RT(5) + Immediate(16)
J 型 无条件跳转 OP(6) + Address(26)

所有指令统一 32 位固定长度。

5.2 设计的规整性

  • OP 码统一 6 位,分布在指令的固定位置
  • R 型指令有额外的 FUNCT 字段(6 位)作为扩展 OP
  • I 型指令的立即数 16 位覆盖 ±32K(64K 范围)
  • J 型指令 26 位地址字段,实际使用前左移两位 → 28 位寻址空间

MIPS 非常清晰——取指后检查高 6 位就知道指令类型,R 型全 000000,J 型和 I 型分开。写 RISC-V decoder 觉得还好,但看了 MIPS 以后——规整是真规整,写起来比 RISC-V 还简单。

5.3 OP 码的空间分配

MIPS 把 OP 码固定为 6 位 = 64 个可能值:

  • OP = 000000 → R 型(用 FUNCT 字段区分具体运算)
  • 若干 OP 值 → J 型(跳转)
  • 若干 OP 值 → load/store
  • 其余全部 → I 型

R 型多出 2^6 = 64 个 FUNCT 空间,极大扩展了 R 型指令的种类。

5.4 为什么”其余全部归 I 型”会有问题

  • 当需要新增一条非 I 型指令时,没有办法”师出有名”地放在 I 型空间里
  • 放进去就和 I 型语义冲突——这就叫积重难返
  • RISC-V 因此设计了六种指令格式(而 MIPS 只有三种),就是为了更灵活地填新指令

六、RISC-V 与 MIPS 的对比

6.1 RISC-V 的设计现状

RISC-V 设计上的一些特点:

  1. 立即数只有 12 位:MIPS 是 16 位,12 位不是一个完整的字节倍数,显得别扭。设计者的思路是”超过 16 位本来就得去 memory,不如用 12 位,省下的空间给指令扩展”。
  2. OP 码/FUNCT 字段分两段:不像 MIPS 那样整整齐齐都放在高 6 位,RISC-V 把控制字段分布在指令的不同位置。
  3. 末尾 2 位区分 16/32 位指令:为了兼容压缩指令(Compressed Instruction, 16 位),破坏了统一 32 位的规整性。

RISC-V 搞 16 位压缩指令是为了省钱——”本来需要买个一兆的芯片,现在 512K 就够了”。但就省那么一点,会严重破坏指令集的规整性。

6.2 寻址方式的对比

特性 MIPS / RISC-V x86
寻址方式数量 1 种(基址+偏移) 多种(基址/变址/比例/位移组合)
数组访问效率 需多条指令 单条指令完成
硬件复杂度
编译复杂 高(编译器优化负担重)

RISC 仅支持一种寻址方式——设计起来简单了,但访问数组一定效率低,因为数组是算法里最常见的访问模式。

6.3 ARM 的”臃肿化”教训

  • ARM 一开始也是”精简的”RISC 处理器
  • 随着市场需要不断增加指令:多寄存器加载/存储(LDM/STM)、各种特殊运算指令
  • 越来越臃肿,CPU 设计越来越复杂,拖累执行效率

6.4 指令编码空间的零和博弈

MIPS 三种指令格式的”拉锯”展示了设计中的零和博弈:

  • 想增加 OP 码?(从 6 位变 7 位)→ 寄存器地址减 1 位 → 要么减少寄存器数量,要么减少立即数
  • 想从 32 个寄存器扩展到 64 个?→ 每个寄存器字段从 5 位变 6 位 → R 型三个寄存器字段各多 1 位 → 吃掉 3 位 → 必须从 FUNCT 或 SHAMT 减
  • 哪种方案都不好办

任何一位的增加减少都是问题。寄存器多一倍——但描述就会多一倍。你以为 32 位很宽裕?三个寄存器各 5 位就 15 位没了,OP 码 6 位,剩下的空间其实没有那么大。


七、x86 多媒体扩展:从 MMX 到 SSE

7.1 需求驱动

  • 早期电脑只用于办公和代码,后来大家用电脑听音乐、看视频、打游戏
  • 图像/视频处理涉及大量 8 位短整数运算(RGB 每通道 0-255)
  • 一张 4K 图片意味着 2048*2048 级别的像素量,需要非常高的处理吞吐

7.2 MMX 的诞生

MMX(MultiMedia eXtensions)就是针对多媒体场景增加的专门指令:

  • 8 位 RGB 像素的加法、平均、差值运算
  • 饱和运算(Saturated Arithmetic):比如 0xF3 + 0x1D 按普通算法溢出后除 2 得到一个错误的小值;饱和运算先截断到 0xFF,确保结果不违反物理意义
  • 这种”短整数大量并行”的模式催生了 SIMD单指令多数据,Single Instruction Multiple Data)

7.3 SSE 和不断的指令增长

SSE(Streaming SIMD Extensions)一代代往下加,直到 CPU 能搞定多媒体处理。但很快 GPU 出现了。

7.4 DSP 的专用指令思路

  • DSP数字信号处理器,Digital Signal Processor)最早提出针对 A * B + C → D 这种乘加运算(Multiply-Accumulate, MAC)的专用单周期指令
  • 这是因为信号处理(音频/图像)中这类运算极其频繁

做就要付出代价(付出资源),不做省事儿,但在特定场景就会慢。首先要问:在你的应用里这个特定场景占多大比例?占了很小的比例,投资没意义;占了很大的比例,就要考虑来做。


八、GPU 的崛起与 AI 时代的硬件变迁

8.1 GPU 的核心思路

GPU 和 CPU 走了完全不同的路线:

  • CPU:复杂的控制逻辑、分支预测、乱序执行,每个核心很强但数量有限
  • GPU:非常简单的控制逻辑,每个单元很小——就是几十万个简单 ALU 做加法和乘法——没有复杂控制流
  • 核心是靠量大取胜:几十万个单元同步工作,一幅图像哗哗地处理完

8.2 从游戏显卡到 AI 加速器

  • GPU 最早是为了游戏渲染——让画面更漂亮
  • 但买显卡打游戏的人并不多
  • 直到人工智能出来——神经网络本质就是大量矩阵乘加运算,跟 GPU 的架构完美匹配
  • 英伟达(NVIDIA)因此从游戏公司变成 AI 时代最大的赢家

8.3 英特尔的没落

  • 英特尔在 CPU 领域非常强,自己的编译器 ICC 编译出的代码性能比 GCC 更高
  • 但谁能想到:打败英特尔的不是 AMD(同样做 x86 的竞争对手),而是来自另一个维度的英伟达 GPU
  • 这就叫降维打击——以前觉得会不会打败英特尔的是 AMD,因为两家做同样指令集的竞争,结果直接上来一个 GPU,把指令集掀了桌子

九、异常、中断与特权指令简介

9.1 基本概念

  • 异常和中断在逻辑上不复杂:一条指令执行时发生特殊情况 → 走到别处(异常处理程序)→ 处理完回来
  • 里面涉及到 用户态到内核态的转换——操作系统层面的事

9.2 CPU 层面的需求

  • CPU 只需要知道:(1) 你走了、(2) 你一会儿能回来、(3) 别跑丢了、(4) 去干什么
  • 设计上的选择:
    • 去了再看什么情况?(向量中断:地址里包含异常类型)
    • 还是来的时候就告诉是什么情况?(统一入口:去了再查询)

9.3 RISC-V 的除零处理

  • x86 一旦除零就触发硬件异常中断
  • RISC-V 不设除零异常——除零操作返回固定值 -1(对于有符号除法),程序员自己去检测这个特殊值

这说到底是硬件干还是软件干的问题。RISC-V 的想法就是”做个超简单的硬件,让软件工程师去处理”。但很多程序员说”你别给我报错,你就继续往下执行,出错了是我的事”——所以有了 try/catch 异常处理。


十、寻址方式与数组遍历的效率

10.1 问题的核心

访问二维数组 B[I][J] 时:

  • B 的起始地址是 504
  • B[I][J] 的地址 = 504 + I * 行大小 + J * 元素大小
  • 每一步都要计算地址,然后取数据

这种 “A * B + C” 的计算非常常见——既然它这么简单,能不能专门给它做条指令?

10.2 x86 的”一步到位”寻址

  • x86 可以在一条指令内完成 “基址 + 变址 * 比例 + 位移” 的计算并访问内存
  • for (i=0; i<N; i++) sum += A[i] 这种遍历,x86 用索引寻址比 RISC 更紧凑
  • 但代价是指令更长、decode 更复杂

10.3 数据结构的对齐问题

  • 声明顺序会影响内存布局:char x; int y; char c; double d 的排列会因为对齐而浪费空间
  • 一个 char 占 1 字节,但因为对齐规则,下一个 int 必须从 4 的倍数开始 → 浪费 3 字节
  • 现在大家”不差这点钱”,但以前内存宝贵时精打细算

十一、流水线的数据冒险与缓存延迟

11.1 经典 RAW 数据冒险

考虑代码 G = (A+B) - (C+D) + (E-F) - (G+H)

  • 在单周期 CPU 中:每条指令独立执行完,没有问题
  • 在流水线中:第二条指令需要的操作数 C+D 还没算出来(还在前一条指令的执行阶段)

解决方案

  • 放 bubble(空操作)等待
  • 旁路(Forwarding/Bypassing)直接把 ALU 结果前馈给下一条指令

11.2 Load 指令的延迟问题

load 指令去 memory 取数据:

  • 如果数据在缓存(Cache):很快(3-4 个 clock)
  • 如果数据在主存:可能 50 甚至几百个 clock
  • 不确定需要等多久

所以 load 指令在流水线中是最麻烦的。在 lab 中,memory 模块是简化的——”只要你取,必定有,立马回来”。但真实环境完全不是这样。

11.3 为什么建议保留单周期版本

强烈建议保留一个自己的单周期 CPU。后面加特权指令、加中断、加异常——如果有个单周期的,一条指令执行完再处理中断,逻辑很清晰。如果非要管流水线里五条指令同时在线的时候来中断,你停哪一条?前面几条要不要清掉?复杂度是几何级上升的。


十二、中国 CPU 产业现状与信息安全

12.1 主要国产 CPU 路线

厂商 指令集 授权方 路线
龙芯(Loongson) MIPS 派生(LoongArch) 购买了 MIPS 修改权 自主扩展
华为 ARM ARM 公司 买授权自研
申威/兆芯 x86 AMD 时代购买 兼容 x86
RISC-V 生态 开源 自由使用

12.2 知识产权困局

  • x86 是美国 Intel/AMD 的知识产权,英国 ARM 的也是授权收费
  • MIPS 中国买了修改权限(龙芯有了自己的 LoongArch 指令集),但 MIPS 本身日益式微
  • RISC-V 开源免费——但如果自己魔改,生态就不兼容,别人不给你做编译器支持

12.3 RISC-V 的嵌入式应用

  • RISC-V 目前最广泛的应用在嵌入式处理器上:遥控器、电视机、洗衣机、冰箱、微波炉
  • 正在向桌面处理器/服务器延伸
  • 因为降低成本和开源的属性,嵌入式计算是它的优势领域

12.4 信息安全与自主可控

中国的信息安全防守任重道远,有自己的芯片和操作系统是底线安全。现在跟美国觉得只是普通的商业竞争,但信息安全的防护一定要特别小心。必须要有自己的指令集、自己的芯片。


十三、指令集设计的方法论总结

13.1 新指令要放在哪儿

当需要新增一条指令时,必须问:

  • 它是 R 型、I 型还是 J 型?
  • MIPS 已经说了”其余全部归 I 型”——如果不是 I 型,放在 I 型空间就冲突
  • RISC-V 设计了 6 种格式,给新指令留了更多”合理的位置”
  • 任何一个空白的 OP 码空间,都有理由去填,但必须”师出有名”

13.2 x86 的变长优势

  • x86 想要多少空间就有多少空间,想写多长就多长,扩展能力极强
  • 但付出了变长指令字的痛苦——decode 时每一步都要判断指令格式
  • 已经开到 15 个字节了,还在继续加

这就是 x86 指令集”生生不息”的原因——扩展能力强。社会变化太快了,人工智能这么火,谁知道过两年又冒出什么别的东西。

13.3 没有银弹

  • RISC-V 有优点(开源、灵活)也有缺点(编码不规整、12 位立即数)
  • MIPS 有优点(规整、清晰)也有缺点(扩展空间不足、已被淘汰)
  • x86 有优点(兼容、扩展性强)也有缺点(臃肿、decode 复杂)
  • ARM 有优点(多寄存器加载/存储高效)也有缺点(指令集越来越臃肿)

就像吃了很久家乡饭的人,换了一个菜系总觉得味道不正宗。其实只是这个”神经网络”被训练成了那个模式。习惯了一种东西就总觉得它是最好的。学了 RISC-V 以后可能也会觉得它最好——但每一个设计选择都有道理,关键是你知道它为什么这样选。