16 后端 Codegen 与指令调度洞察
- 章节编号:16
- 所属层:C 编译与算子层(核心壁垒;DSA 自研 codegen 是性能命脉)
- 关联 ADR:ADR-029(后端 Codegen 路径)、ADR-030(静态调度与解耦访问/执行)、ADR-031(片上 SRAM/DMA 分配)、ADR-032(Auto-tuning/Cost model/微内核兜底)
- 上游依赖:02(数据流/精度)、03(SRAM/DMA)、07(张量 CISC ISA)、08(静态调度/显式 DMA)、15(IR/DSA Dialect)
学习目标
- 前置知识:读过 15 章(IR 与 DSA Dialect,知道 tiling/fusion 是在 IR 层怎么表达的)、03 章(片上 SRAM 稀缺、DMA 显式搬运)、07/08 章(张量级 CISC ISA、静态调度与弱序内存语义)。会看 Roofline(知道算子是「算力受限」还是「带宽受限」),写过基础 GEMM 循环即可,无需写过真实编译器后端。
- 学完产出:① 能画出后端 codegen 的完整流水——从 DSA Dialect 到芯片指令,中间经过 tiling → fusion → 片上内存分配 → 指令调度 → DMA 双缓冲这几个关键阶段,并说清每一阶段在解决什么物理约束;② 能用「让 tile 恰好放进 SRAM」一句话讲清 tiling 尺寸为什么不是越大越好,并亲手算一遍一个 GEMM tile 的 SRAM 占用;③ 能量化 fusion 的收益——算出「融合前 vs 融合后」一段
matmul → add → gelu的 HBM 读写次数差,理解为什么 fusion 是带宽受限算子的第一优化;④ 能说清「双缓冲 + 解耦访问/执行」如何用一次 DMA 预取把下一块数据的搬运延迟藏进当前块的计算里;⑤ 能解释为什么后端 codegen 是自研 DSA「不可外包的第一壁垒」。 - 阅读姿势:盯住一条主线——「后端 codegen 的每一个 pass,都是在跟『片上 SRAM 太小、HBM 太慢』这两堵墙讨价还价」。tiling 是为了让数据放得进 SRAM;fusion 是为了不把中间结果写回 HBM;双缓冲是为了让计算单元在等数据时不空转。看懂这条主线,ADR-029~032 的所有取舍就都串起来了。
1. 范围与目标
后端把 DSA Dialect(15 章) 降为芯片可执行的高效指令。DSA + 编译兼容 ⇒ 性能在很大程度由自研 codegen 决定(无论上层是框架直降还是 Triton 前端),这是不可外包的第一壁垒。
核心问题
- 后端路径:复用 LLVM 还是自定义汇编?张量引擎指令如何生成?
- 指令调度(软流水)、寄存器/片上 SRAM 分配、DMA 编排如何做?
- 如何实 现 静态调度 + 解耦访问/执行(确定性 + 延迟隐藏)?
- Auto-tuning + cost model 与手写微内核兜底如何配比?
2. 需求洞察(承接上游)
- 张量级 CISC + 向量/标量(07 章) ⇒ 后端做张量指令选择 + 向量/标量指令 + repeat/解耦语义。
- 软件管理 scratchpad + 显式 DMA + 弱序(08 章) ⇒ codegen 自动编排片上内存分配 + DMA 搬运 + 同步插入(最难)。
- 确定性(01/08 章,Groq 印证) ⇒ 关键路径静态可调度、时序可预测。
- batch≈1(01 章) ⇒ tiling/软流水把小 GEMM/算子打满,隐藏访存延迟。
- 混合精度/块缩放(02 章) ⇒ 指令级支持 INT4/8/FP8 + 块缩放 + BF16 累加。
- 编译兼容(07 章) ⇒ 每代重调,需 cost model + auto-tuning 保性能可移植。
2.1 后端 codegen 全流水:一张图看清「Dialect → 指令」
后端不是一个黑盒,而是一串目的明确的 pass。下图把 DSA Dialect 降到芯片指令的关键阶段拆开,标注每一阶段跟哪堵物理墙对话:
读这张图的关键:tiling 与内存分配对着「SRAM 太小」这堵墙,fusion 与 DMA 编排对着「HBM 太慢」这堵墙。后端的性能上限,就是这几个 pass 跟两堵墙讨价还价的结果——这也是为什么 codegen 无法外包:墙的位置(SRAM 容量、DMA 带宽、张量单元形状)每代芯片都在变,pass 的启发式必须跟着重调。
3. 技术现状与趋势(点名 + 来源)
3.1 结构化 Codegen(MLIR/IREE)
- MLIR/IREE 的 structured codegen(Nicolas Vasilache 等)是 IREE 的核心;把 tiling/fusion/数据布局沉淀进 MLIR,后端生成各硬件高效 kernel。
- 判断:复用 MLIR structured codegen 框架,自研 DSA 后端(指令选择/调度/分配)。
3.2 全代码生成 + cost model(降手写依赖)
- PolyBlocks:分析性 cost model + 启发式,多级 tiling、片上 scratchpad 使用、映射 matmul/conv 到矩阵单元、attention 融合;JIT 媲美/超过 TorchInductor/XLA,单算子 competitive with cuDNN/cuBLAS。
- TPP/libxsmm:Linalg→XSMM 微内核,编译器做 tiling/fusion/layout,底层用微内核做向量化/指令选择。
- 判断:编译器自动生成为主(cost model + auto-tuning),手写微内核/算子兜底(17 章),高频 pattern 回流为模板。
3.3 静态调度 + 解耦访问/执行(确定性 + 延迟隐藏)
- TPU:解耦访问/执行(地址发出即完成,重叠 DMA 与计算)。
- Groq:编译期把每条指令/搬运/同步排到时钟周期,零抖动确定性(02 章 3.5)。
- 判断:关键实时路径采静态调度(确定性);通用路径软流水 + 解耦访问/执行隐藏访存延迟。
3.4 后端基座:LLVM
- LLVM 提供成熟的指令调度/寄存器分配/RISC-V 后端;张量引擎为自定义指令/intrinsic。
- 判断:RISC-V 标量/控制走 LLVM 后端;张量/向量自定义 codegen(可经 LLVM IR 或自定义汇编)。