数据截至 (上游 commit 118e812b316f)
04 · codegen 与渲染器
这一章讲什么: 第 3 章切出的每个 kernel(一棵
SINKAST)怎么变成能在设备上跑的二进制。前半是「怎么优化一个 kernel」(轴类型变换 + BEAM 搜索),后半是「怎么把它打印成代码」(渲染器家族)。
1. 它要解决的小问题
调度器给的 AST 是「数学上正确」的:一堆 RANGE 循环套着 LOAD/ALU/STORE。但直接照抄成代码会很慢——GPU 要快,需要决定:
- 哪些循环维变成线程网格(global/local),哪些展开(upcast/unroll),哪些塞进 TensorCore;
- 访存怎么合并(coalescing)、局部内存(shared/local)怎么用;
SIN/EXP2这 类目标不支持的算子怎么分解(decomp)。
这些决定既依赖数学结构,也依赖具体设备——所以 codegen 分两层:与设备无关的 lowering + 设备专属的渲染。
2. 管线总览
入口 to_program(tinygrad/codegen/__init__.py:506-508,带磁盘级缓存),主体 full_rewrite_to_sink(tinygrad/codegen/__init__.py:286-405)。约二十步,按性质分四段:
SINK(AST,来自调度器)
│ A. 优化段(可被 BEAM 控制)
│ load collapse → split/simplify ranges → apply_opts
│ (hand_coded 启发式 或 BEAM 搜索,给 RANGE 改轴型)
▼
B. 展开段
│ expander(广播/REDUCE 展开)→ reduce→acc 寄存器累加
│ → STAGE→local buffer → add_gpudims(GLOBAL/LOCAL→SPECIAL)
▼
C. lowering 段
│ 广播展开 → devectorize → memory coalescing
│ → 弱 dtype 落地 → decomp(超越函数/dtype 分解)
│ → 隐式 barrier → 控制流(IF/END 结构化)
▼
D. 成程序段(do_to_program)
│ linearize(优先级拓扑排序成指令列表)
│ → render(渲染器打印源码)→ compile(外部编译器出二进制)
▼
PROGRAM(SINK + LINEAR + SOURCE + BINARY)
每一步都是一次 graph_rewrite,函数体里有逐步的 name= 标注(tinygrad/codegen/__init__.py:292-405),配 VIZ=1 可以逐步回放。下面挑灵魂步骤讲。