计算机体系结构可视化

计算机用固定宽度的位和解释规则表示数值。八位无符号数中的 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 代码检查轨迹。