从 Bit 到 AGI第一章:比特(Bit)

CPU 如何理解世界

同一段 0101,可以是数字、指令或像素——解释规则决定含义。

作者 David更新于 第 3 篇(共 186 篇)

内存里某地址的 8 个字节是 01001000 01100101 01101100 01101100 01101111——它可以是整数 72、字符 'H',也可以是某条机器指令的操作数。硬件本身不自带「意义」,CPU 靠指令集和程序约定给 bit 模式赋予含义。

本篇说明:取指、译码、执行如何运转;数字、指针、指令如何在同一套 bit 流里共存。

直觉:同一本字典,不同读法

一串汉字「银行」:在金融文章里是金融机构,在地理文章里可能是河岸。bit 串也一样:0x48 在 ASCII 表是 'H',在 int8 解释下是 72,在机器码里可能是某条 mov 的一部分。解释上下文(类型、段寄存器、当前指令)决定读法。

定义:冯·诺依曼架构要点

  1. 程序与数据同存于可寻址内存(统一地址空间)。
  2. PC(Program Counter)指向下一条要执行的指令地址。
  3. 指令周期:取指(Fetch)→ 译码(Decode)→ 执行(Execute)→ 写回(Writeback),现代 CPU 流水线并行多指令,主频常见 2–5 GHz(每核每秒数十亿周期)。

典型 64 位 x86-64 寄存器:RAX、RBX… 各 64 bit;通用寄存器 16 个量级,远小于内存(GB 级)——所以要有 Cache 与内存层次(后文详述)。

例子一:加法在硬件里是什么

伪代码 a = b + c,编译后可能变成:

; 示意:b 在 RDI,c 在 RSI,结果写 RAX
mov  rax, [rdi]   ; 从内存读 b 的 8 字节到 RAX
add  rax, [rsi]   ; RAX += 内存中的 c
mov  [rdx], rax   ; 结果写回 a 的地址

add 在硅片里是加法器:对两个 64 bit 输入逐位全加器链,带进位,1 个时钟周期量级(实际还依赖流水线深度)。没有「加」这个中文字,只有 bit 模式 000000xx 触发 ALU 走加法通路。

例子二:同一段内存,两种视图

import struct
# 4 字节:小端存 int32 值 16909060 (0x01020304)
raw = bytes([0x04, 0x03, 0x02, 0x01])
as_int = struct.unpack('<i', raw)[0]
as_bytes = list(raw)
print(as_int, as_bytes)  # 16909060 [4, 3, 2, 1]

C 语言里 (int*)p 与 (char*)p 访问同一地址,读出的「意义」不同——这就是类型 punning 的底层原因(严格别名规则在语言层限制此类写法)。

量化:一次「理解」有多快?

指标典型值(桌面 CPU,2024 量级)
主频3–5 GHz
每周期 IPC~2–4(依赖 workload)
L1 Cache 延迟~4 cycles ≈ 1 ns
主内存 DRAM 延迟~100 ns
L3 命中 vs 内存 miss差 30–50 倍 很常见

CPU「理解」世界的方式是:在纳秒级把 bit 当作指令执行,或当作数字运算;慢的是等内存(AI 训练里 GPU 也在和同样的问题搏斗)。

分支与预测:世界不只有线性

def abs_val(x):
    if x >= 0:   # 编译成 cmp + 条件跳转
        return x
    return -x

if 会变成「比较 + 跳转指令」。分支预测错一次,流水线 flush 可能损失 10–20 周期。循环友好的数值代码(矩阵乘)比分支杂乱的业务逻辑更「CPU 友好」——后面 GPU/SIMD 篇会呼应这一点。

指令集:x86 与 ARM 的「方言」

同一串 bit,在 x86-64 与 ARM64 上可能是不同指令——这叫 ISA(Instruction Set Architecture)。Apple M 系列跑 ARM64;云服务器 x86 居多;AWS Graviton 是 ARM。Python/PyTorch 轮子要分平台编译,因为机器码 bit 模式不兼容。

典型 x86 指令长度 1–15 字节 变长;ARM64 常固定 4 字节,解码更简单,功耗更低。AI 推理在 GPU 上跑 CUDA(NVIDIA 私有 ISA),CPU 负责喂数据——heterogeneous computing 里每种处理器各认各的 bit 方言。

想深入到具体指令,各家 ISA 的官方参考手册:

栈、堆与寄存器:bit 住在哪里

函数调用时,返回地址、局部变量压在 栈(向下增长);malloc/new 的对象在 堆。寄存器只有 几十×64 bit,装不下 16 GB 模型——大数组必在内存,寄存器暂存循环变量和指针。

// 示意:指针 p 本身是 8 字节,指向堆上 1e9 个 float 的的首地址
float *p = malloc(1e9 * sizeof(float));  // ~4 GB

CPU「理解」指针 = 把 8 字节当作无符号整数地址,经 MMU 查页表得到物理地址,再读 DRAM——每一次数组访问都是 bit 地址解码。

微架构一瞥:超标量与乱序

现代 CPU 每周期可 退休多条指令(IPC>1):乱序执行在依赖允许时先算后面的 independent 指令。分支预测错则 flush 10–20 周期——Attention 里 if (mask) 少,GEMM 内层循环无分支,故 AI 热点 kernel 喜 CPU/GPU 无分支 SIMD。

Out-of-order 不改变语义:as-if sequential,程序员仍见单线程内存模型(C11 memory_order 另论)。多线程写同一 cache line 才需 atomic——PyTorch 分布式训练 all-reduce 走 NCCL,不靠 CPU 乱序救场。

场景实战:用 bit 视角读一条新闻

「某公司发布 405B 参数模型」——你应立刻换算:FP16 约 810 GB 权重,INT4 量化约 200 GB,单卡不可能,推理需 多卡 TP 或 MoE 激活子集。「在 10 TB 文本上训练」——UTF-8 磁盘 ~10 TB,token 数约 2–4 T(视语言混合),训练 FLOPs 量级 6ND6ND(Kaplan,NN 参数 DD token)。新闻里的每个形容词都可换成 byte 和 FLOPs。

常见误区

  1. 「CPU 直接运行 Python」——Python 字节码由解释器或 JIT 转成机器码;你写的是语义层,CPU 只看机器码。
  2. 「64 位 CPU 只能算 64 位以内的数」——寄存器宽度是一次运算的「车道宽」;大整数、浮点、向量有专用指令扩展。
  3. 「内存里某处放着字符串 'Hello'」——存的是编码整数序列;是 'H' 还是 72,取决于 load 指令后怎么用。

动手试一试

观察整数与 ASCII 的同一性(仅用于理解,勿在生产搞混类型):

n = 0x48656C6C6F  # "Hello" 若按大端打包成 5 字节需截断,这里演示逐字符
chars = [0x48, 0x65, 0x6C, 0x6C, 0x6F]
print(''.join(chr(c) for c in chars))
print(sum(c << (8*i) for i, c in enumerate(reversed(chars))))

小结

CPU 通过指令集架构把 bit 解释为操作与操作数;程序通过类型与 ABI 约定把 bit 解释为整数、浮点、指针、字符串。AI 推理程序最终也只是一串这样的指令,在数十亿晶体管上驱动矩阵运算。

下一篇:整数如何表示——有符号、无符号与溢出的精确规则。

本页目录