TileLang — 写给 GPU 的方言
V4 的细粒度架构会产生数百个 ATen operators。TileLang 通过轻量 host code、编译期验证和显式布局控制降低调度开销,并在需要时支持与 CUDA 对齐的位级输出。
TileLang = 一种把 GPU kernel 写成 tile-级 Python DSL 的语言,自带 SMT 求解器、host codegen、位级可复现的 lowering 钉死机制
一句话:把"写 CUDA"变成"写 Python 描述 tile 怎么搬",编译器负责把 host 代码静态生成、用 Z3 解决索引非线性、用 layout 标注钉死代码生成顺序。 报告给出的结果是:CPU 侧参数验证从几十到数百微秒降至低于 1 微秒。通过让简化规则、layout 与 lowering 对齐 CUDA 实现,TileLang 可以在需要时生成位级一致的输出;这不等于 SASS 指令字节完全相同。
- ATen(被替代的对象)
- PyTorch 的 ATen 算子层会进行参数检查、调度和 kernel launch。V4 报告称细粒度算子使 CPU 侧验证开销不可忽略,但没有给出“每次 30–100µs”或“占 wall-time 30%”的统一测量。
- Host 开销 vs Device 开销
- Device 是 GPU 执行 kernel 的时间;Host 是 CPU 侧验证、参数封送和发起调用的开销。kernel 越短,固定 host 开销占比通常越高。报告量化的是 CPU-side validation 低于 1µs,不是整个 CUDA launch 的物理下限。
- DSL(Domain-Specific Language)
- 领域专用语言。TileLang 提供面向 tile、shared memory、barrier 与 reduce 等 GPU kernel 抽象的编程接口,让编译器能够生成和优化设备代码。
- Z3 / QF_NIA(编译器里的 SMT 求解器)
- Z3 是微软的 SMT 求解器(Satisfiability Modulo Theories)。QF_NIA = "Quantifier-Free Nonlinear Integer Arithmetic" —— 处理无量词、整数、含乘除的算术约束。TileLang 用它判断"这个张量索引 `i*M + j*K + k` 在所有合法 `(i,j,k)` 下是否唯一"等问题,从而决定是否能向量化、能否合并访存。把传统编译器需要"启发式 + fallback"的部分,换成可证明的精确判断。
- T.annotate_layout(钉死 lowering)
- TileLang 允许开发者显式标注布局,并让编译器的简化与 lowering 规则贴近 CUDA 实现,从而在需要时获得位级一致的输出。报告没有声称每次编译都会生成与手写 CUDA 完全相同的 SASS。
- IEEE-754 严格 vs fast-math
- TileLang 默认关闭编译器级 fast-math,把影响精度的近似操作设为显式 opt-in;需要严格语义时,还提供带明确舍入模式的 IEEE-754 intrinsics。fast-math 可能改变运算与舍入结果,但不等于结果随机。
- 位级可复现(bit-identical reproducibility)
- 在指定输入、实现与执行条件下得到逐 bit 一致的输出,比数值近似一致更严格。它便于核对 TileLang 与 CUDA baseline,也有助于复查训练异常;并非所有调试都必须以逐位一致为前提。
1. 为什么要减少细粒度 ATen 调用
通过 Python / ATen 调用细粒度 kernel 时,运行期检查和参数封送会产生固定成本。对短 kernel,这类 CPU 开销更容易限制利用率。V4 的新模块又会展开成大量细粒度操作,因此作者引入 TileLang host codegen:
- 架构侧:mHC、hybrid attention 与低精度路径都需要定制 kernel;
- 调用侧:模型结构会展开成数百个 ATen operators,放大逐次 validation 的固定成本;
- 编译侧:复杂 layout、索引和数值语义需要比普通算子组合更强的分析能力。
示意:当一层由大量细粒度 operators 组成时,每次参数验证与 launch 的固定成本会累积。V4 报告只说架构会产生数百个 ATen operators,没有公布每层实际 launch 数、device 时间或 wall-time 占比。
2. TileLang 怎么做到 < 1 µs
三件事:
- 共同生成 device kernel 与轻量 host launcher:把 dtype、rank / shape 约束和 stride / layout 假设写入生成代码;
- TVM-FFI 调用约定:通过紧凑接口与 zero-copy tensor interop 降低参数封送成本;
- 移出 Python 热路径:运行时验证和 marshaling 在生成的 host code 中完成。
把一次 kernel 调用的 host 时间拆开:
这只是概念拆分。V4 报告没有分别给出 $T_{\text{py}}$、$T_{\text{disp}}$ 与 $T_{\text{launch}}$;它测得的是 CPU-side validation overhead 从几十或数百微秒降至每次调用低于 1 微秒。
3. 把 Z3 SMT 搬进编译器:QF_NIA 解决索引非线性
传统 GPU 编译器(NVCC、TVM)面对张量索引时,用启发式判断"两次访存能不能合并"、"循环能不能向量化"。启发式的问题:能处理简单情况(i, i+1 相邻),但碰到 i*M + j*K 这种带乘法的就退化为保守(不敢合并),白白浪费带宽。
TileLang 把这步换成可证明:用 Z3 求解器对索引表达式建立 QF_NIA 约束,问"是否存在两个不同 thread 写入同一地址"。
例子:要判断 idx = i*M + j($0 \le i < N$, $0 \le j < M$)能否安全向量化(即不同 thread 写不同地址)。SMT 编码:
翻译成大白话:"存在两组下标,下标本身不同但映射到的地址相同" —— 如果存在解 → 有冲突,不能向量化;不存在解 → 安全。
SMT solver 可以形式化判断这类整数表达式是否满足所需性质,并把结果用于 layout inference、memory hazard detection、bound analysis、vectorization 与 barrier insertion。报告只说在合理资源限制下,整体编译开销控制在数秒;没有给出单次查询 10 ms,也没有承诺自动找到全局最优 layout,开发者仍可用 T.annotate_layout 固定布局决策。
读图法:每条横向条形是一组教学时间预算。红、黄、橙、紫段的数值是为了演示固定 host 成本,不是报告实测,也不是硬件下限;蓝段表示可调的 device 时间。
把 device 时间调短,可以观察固定 host 成本占比上升。可确认的论文结果只有 validation overhead 低于 1µs;端到端差异仍取决于 kernel、硬件、调用路径和 workload。
4. 数值精度与按需位级复现
浮点加法不满足结合律:$(a+b)+c \ne a+(b+c)$,因此代数化简、lowering 与布局选择都可能改变最后几位。若要与手写 CUDA baseline 逐位比较,编译器需要尽量保持相同的求值与累加顺序。
TileLang 默认关闭编译器级 fast-math,把影响精度的近似作为显式 opt-in 操作,例如 T.__exp、T.__log 和 T.__sin。需要严格 IEEE-754 语义时,还可以使用带明确舍入模式的 T.ieee_fsqrt、T.ieee_fdiv 和 T.ieee_add。配合 T.annotate_layout 和对齐 CUDA toolchain 的化简、lowering 规则,可以在需要时得到与参考 CUDA 实现位级一致的输出;这不等于生成完全相同的 SASS。
与参考 kernel 位级对齐,可以把差异定位到具体算子,而不必先判断偏差来自算法、布局还是浮点重排。报告没有给出某个训练 step 的调试案例,也没有声称 TileLang 单独保证整次训练从 checkpoint 开始逐字节复现。
本章小结
- V4 的细粒度 operators 让 host 侧验证和调度值得专门优化;报告未给出其占 wall-time 的百分比。
- TileLang 的 host codegen 将运行时检查和参数封装移出 Python 路径,使 CPU 侧 validation overhead 从几十或数百微秒降到每次调用低于 1 微秒。
- Z3 SMT 进编译器解决张量索引的非线性约束,把启发式的保守换成可证明的精确。
- 通过显式 layout 与对齐 CUDA 的简化/lowering 规则,TileLang 可以在需要时获得位级一致的输出;不应扩写成“同一 SASS”。