计算机体系结构可视化
计算机用固定宽度的位和解释规则表示数值。八位无符号数中的 255 加 1 回到 0,补码最高位可表示负权,减法可通过加法实现,原因都在表示规则和有限字宽。
画面把位固定在标注位置,显示字边界,也显示离开字宽的进位,而不是把它隐藏。
课程比较
| 课程 | 主要问题 | 变化 | 失败边界 |
|---|---|---|---|
| 二进制数 | 一排位表示什么 | 位置 k 的权重是 2^k | 八位的 255 + 1 回到 0 |
| 补码 | 如何表示负数 | 最高位从 +2⁷ 改为 −2⁷ | 127 + 1 回到 −128 |
| IEEE 754 浮点数 | 如何表示小数 | 分成三个字段,用指数移动小数点 | 0.1 + 0.2 可能不等于 0.3 |
| 布尔逻辑与逻辑门 | 加法由什么完成 | 逻辑门真值表按连线组合 | 换错一个门,四行中仍有三行正确 |
| 指令执行 | 什么让一个字成为指令 | PC 指向内存字,取指循环解释它 | 存入的数据也可能被取作指令 |
| CPU 流水线 | 五段重叠为什么不是每条指令便宜五倍 | 同时运行五段,每周期发射一条 | lw 的结果供紧邻指令使用时仍需等待 |
| 缓存层次 | 十二次读取为何是 804 或 3072 周期 | 地址分散到不同数量的缓存行 | 步长 4 为 3072,步长 1 为 804 |
| 分支预测 | 未判定的 if 如何继续取指 | 先猜路径,猜错后丢弃推测工作 | 不可预测分支浪费已经开始的工作 |
前两课都按置位的位置加权求和,补码只改变最高位的权重,因此同一加法电路无需检查正负,就能产生对应的固定位宽结果。
浮点数则把字分成符号、指数和尾数字段。它用有限位宽换取很大范围,但可表示值不再等距,许多十进制小数只能舍入。逻辑门课程进一步拆开加法器,说明前面抽象的进位如何由电路产生。
建议学习顺序
先学二进制数。除以 2 逐次取出低位,把置位的权重相加则还原数值。计数模式展示进位如何穿过连续的 1。到 255 加 1 时,八位全部清零,进位没有第九个位置可存。
补码只改变最高位权重。先对几个值取负,再把负数与正数相加,观察加法器无需判断符号。有符号溢出与丢弃最高进位是不同判断,应按表示范围检查。
IEEE 754 浮点数存储 0.1,展示无限二进制展开和实际保存值。再运行 0.1 + 0.2,切到 binary32 场景:字段宽度和舍入改变后,本例与 0.3 的相等比较从假变真。没有“小数十分之一必然如何”的规则,关键是各步舍入落在哪里。
布尔逻辑与逻辑门从真值表组合加法。两输入门有四种输入,连线决定组合后的表。错误加法器换掉一个门,仍有四行中的三行通过,说明少量正确例子不等于电路正确。
指令执行把同样的固定宽度字解释为操作码和三个字段。循环取 PC 指定的字、推进 PC、解码字段、执行操作。对同一跳转指令分别观察跳转与不跳转,再看自修改程序把数据写到原 HALT 位置,随后执行该字。本教学机器没有独立的代码类型标记,PC 读到哪里,就尝试把哪里当指令。
CPU 流水线让五条指令分别处于五个阶段。默认第 5 周期出现 WB MEM EX ID IF。之后第四条指令需要第三条尚未写回的寄存器,等待两周期,执行段出现空洞。切换转发硬件,相同五条指令从 13 周期缩为 10 周期,因为可从流水寄存器读取结果;一次加载相关停顿仍存在,因为加载值在访存段之后才可用。
分支预测在条件尚未解析前猜测路径并继续取指,猜错后丢弃错误路径工作。默认循环的两位计数器开头付出两次代价,之后的循环重入体现其保留倾向的收益。排序与打乱场景使用相同代码和数值,分别耗时 52 和 62 周期,说明排列会改变分支行为。
最后学缓存层次。代码始终执行同一 load(),只有地址序列变化。第一次未命中一次带回四个字。步长 1 的十二次读取涉及三行,共 804 周期;步长 4 涉及十二行,共 3072 周期,接近四倍。冲突场景中,四行有三行空着仍反复替换;改成两路映射可解决本例冲突。
观察重点
- 位 3 是位置,8 是它的权重。最左边是位 7,不能反向加权。
- 本课固定宽度加法会丢弃越界进位。无符号回绕、有符号溢出不是同一判断。
- 补码保留了大部分二进制运算规则,只改变解释方式。
- 整数等距,浮点间隔随指数区间增大而变大;这解释舍入、吸收,以及计算结果使用
==时需要谨慎的原因。 - 进位既可作为算法规则观察,也可继续拆成逻辑门的连线。
- 指令执行中要看 PC 被谁更新,跳转通过改变执行位置实现控制流。
- 停顿不是指令变慢,而是某个阶段在该周期没有有效工作;空泡会沿流水线移动。
- 分支预测错误可以保持程序结果正确,却增加重做的工作,通常不会作为源码级异常出现。
- 缓存搬运的是整行,不只是请求的一个字。后续访问同行内容可复用已经取回的数据,避免新的下层读取,但缓存命中仍有成本。
全部体系结构可视化课程
选择课程,改变数值或场景,逐步对照 Python 代码检查轨迹。