跳转至

从模型到芯片:数据流、ISA 与 AI 编译器

AI 编译器的核心任务是把“对整个 Tensor 做什么”逐步变成“哪些处理单元,在什么时间,从哪里取哪一块数据,执行什么指令”。这条链路连接模型语义与芯片微架构。

端到端编译路径

flowchart TB
    MODEL["PyTorch / JAX / ONNX<br/>模型与动态图"] --> GRAPH["Graph IR<br/>算子、Shape、控制流"]
    GRAPH --> GOP["图级优化<br/>常量折叠 / 融合 / Layout / 量化"]
    GOP --> TIR["Tensor / Loop IR<br/>迭代域、归约、Buffer"]
    TIR --> SCH["Schedule / Mapping<br/>Tile / Fuse / Vectorize / Parallel"]
    SCH --> KIR["Kernel / Target IR<br/>线程、共享内存、矩阵指令"]
    KIR --> ISA["ISA / Binary<br/>PTX → SASS,或 NPU 命令流"]
    ISA --> RT["Runtime<br/>内存、Stream、Launch、同步"]
    RT --> HW["Microarchitecture<br/>Core / SRAM / Cache / NoC / HBM"]

    HW -. 规格与代价模型 .-> SCH
    HW -. Profile 反馈 .-> GOP

每次 Lowering 都在减少抽象自由度:高层只描述等价计算,低层逐渐固定 Layout、循环顺序、Tile 大小、并行层次、存储空间与目标指令。

编译器各层分别优化什么

层 仍然看得见的语义 典型变换 主要约束
Graph IR Conv、Attention、LayerNorm 等算子 融合、常量折叠、算子分解、量化插入 数值等价、动态 Shape、算子覆盖
Tensor/Loop IR 迭代域、Reduction、Buffer Loop Interchange、Tiling、Vectorize 依赖、复用、并行粒度
Kernel IR Thread/Warp/Block、地址空间 Shared Memory Staging、Pipeline、同步 寄存器、Shared Memory、occupancy
Target ISA 标量/向量/矩阵、Load/Store 指令选择、寄存器分配、指令调度 ISA 语义、发射与流水线资源
Runtime Kernel 与 Buffer 的依赖图 内存复用、异步执行、Graph Capture 生命周期、设备拓扑、同步开销

融合不是越多越好

融合可以减少中间 Tensor 和 Kernel Launch,但也可能增加寄存器压力、拉长 Live Range、限制并行度或失去高性能库实现。是否融合必须回到目标形状和微架构资源验证。

几个主流抽象的位置

flowchart LR
    FRAME[模型框架] --> MLIR[MLIR Dialects<br/>多层可扩展 IR]
    FRAME --> TVM[TVM Relax<br/>高层图 IR]
    TVM --> TENSORIR[TVM TensorIR<br/>张量程序与调度]
    MLIR --> LLVM[LLVM IR / Backend]
    TENSORIR --> LLVM
    FRAME --> TRITON[Triton<br/>Blocked Kernel Program]
    TRITON --> GPU[GPU Target]
    LLVM --> CPU[CPU / Target]
  • MLIR 是构建多层 IR 与 Dialect/Pass 的基础设施,不是单一“AI 编译器产品”。它允许保留领域语义后再逐步 Lowering。
  • TVM 用 Relax 表达高层图,用 TensorIR 表达和调度低层张量程序,再面向 LLVM、CUDA 等 Target 生成可执行模块。
  • Triton 让开发者用 Python 写 Block/Tile 级 GPU 程序,编译器负责进一步映射到线程、内存与目标 GPU 指令。
  • LLVM 擅长较低层的通用优化、指令选择和代码生成;高层 Tensor 语义通常在进入 LLVM 前处理。

这些系统并非简单替代关系。实际栈常把多个 IR 与 Backend 串联起来,关键在于每层是否保存了下一步优化所需的信息。

动态 Shape 为什么难

静态 Shape 下,编译器可以针对固定的 M/N/K 搜索 Tile、预分配 Buffer、选择专用 kernel。动态 Shape 会扩大决策空间:

  • 一个 Tile 对大 Shape 高效,对小 Shape 可能浪费线程;
  • 融合后的 Buffer 和寄存器压力随 Shape 改变;
  • 向量化、Tensor Core 对齐和 Padding 条件不再恒定;
  • JIT 特化能恢复性能,却带来编译延迟与缓存管理。

常见策略是 Shape Guard + 多版本特化 + 通用回退路径,而不是幻想一个 kernel 对所有形状都最优。

建立软硬件契约

为一颗新加速器设计编译器接口时,至少定义:

契约 需要明确的内容
算子/指令 支持的数据类型、Shape、累加精度、舍入/饱和语义
存储 地址空间、对齐、Bank、DMA 粒度、一致性与容量
并行 Grid/Tile/队列层次、同步范围、原子与 Barrier
运行时 Buffer 生命周期、命令提交、事件、错误和设备发现
代价模型 延迟、吞吐、带宽、启动成本和资源占用的估计接口
可验证性 IR verifier、数值对照、反汇编、profile counter

编译结果如何验收

  1. 数值正确:按数据类型与近似策略设置容差,并覆盖边界 Shape。
  2. IR 可解释:保存关键 Pass 前后的 IR,确认预期融合、Layout 和量化确实发生。
  3. 代码可确认:检查生成 kernel/汇编是否使用目标矩阵指令和访存路径。
  4. 资源可解释:记录寄存器、Shared Memory、occupancy 与中间 Buffer。
  5. 端到端有效:把编译时间、Warmup、数据转换和 Runtime 开销计入,而不只报热点 kernel。

延伸阅读