从 TVM 到 TileLang:一文读懂深度学习编译器为什么走向 Tile 化

大模型时代,我们经常会接触到这样一些技术:

PyTorch、CUDA、cuBLAS、FlashAttention、Triton、TVM、TileLang。

它们看起来都和 GPU 计算有关,但实际上处在完全不同的层次。

例如下面这一行 PyTorch:

python 复制代码
C = torch.matmul(A, B)

从程序员视角,它只是一次矩阵乘法。

但真正落到 GPU 上,需要解决的问题远比 A × B 复杂:

text 复制代码
矩阵应该如何切块?
        ↓
一个 Block 算多大的 Tile?
        ↓
一个 Block 使用多少线程?
        ↓
数据什么时候从 HBM 搬到 Shared Memory?
        ↓
Shared Memory 怎么布局?
        ↓
什么时候放进 Register?
        ↓
Tensor Core 怎么使用?
        ↓
load 和 compute 能不能流水线并行?
        ↓
如何减少访存?
        ↓
如何隐藏内存访问延迟?

这些问题,构成了深度学习编译器和 GPU Kernel 优化的核心

TVM、Triton、TileLang,本质上都是在回答同一个问题:

怎样用更高层的方式描述计算,同时让编译器生成接近手写 CUDA 的高性能代码?

只是三者选择的抽象层次不同。


一、为什么深度学习需要专门的编译器?

传统程序的编译过程通常是:

text 复制代码
C/C++
 ↓
LLVM IR
 ↓
机器码
 ↓
CPU

但是深度学习程序有一个非常明显的特点:

它大量由张量运算组成。

例如:

text 复制代码
MatMul
Conv
RMSNorm
LayerNorm
Softmax
Attention
GEMM
Reduce
ElementWise

这些运算背后存在大量可以利用的结构信息。

例如矩阵乘法:

C_{i,j}=\\sum_k A_{i,k}B_{k,j}

如果直接让传统编译器处理三层 for 循环:

cpp 复制代码
for (int i = 0; i < M; i++)
    for (int j = 0; j < N; j++)
        for (int k = 0; k < K; k++)
            C[i][j] += A[i][k] * B[k][j];

编译器虽然知道这是循环,却未必知道:

text 复制代码
这是 GEMM
可以 Tile
可以使用 Shared Memory
可以使用 Tensor Core
可以做 Warp-level MMA
可以进行软件流水线

深度学习编译器的意义,就是保留并利用这些张量计算语义


二、TVM 解决了什么问题?

Apache TVM 可以理解为一套面向机器学习工作负载的编译基础设施。

当前 TVM 的整体结构,大致可以理解为:

text 复制代码
PyTorch / ONNX / 其他模型
            │
            ▼
        Relax IR
        模型/计算图级
            │
      图优化 / Fusion
            │
            ▼
        TensorIR
        Kernel / 算子级
            │
     Schedule / Lowering
            │
            ▼
 CUDA / LLVM / OpenCL ...
            │
            ▼
       GPU / CPU / NPU

当前 TVM 官方架构中,高层模型程序主要由 Relax 表达,而低层 tensor program 由 TensorIR 表达。Relax 负责图级结构和优化,TensorIR 则包含循环、Buffer、线程绑定、张量指令等更加靠近硬件的信息。(Apache TVM)

所以如果你看到一些较老的 TVM 教程写:

text 复制代码
Relay
  ↓
TIR

可以把它理解成 TVM 历史上的经典架构。

目前理解 TVM 更合适的方式是:

text 复制代码
Relax
  ↓
TensorIR

三、TVM 最重要的思想:Compute 和 Schedule 分离

这是理解 TVM 的关键。

假设我们计算:

C=A\\times B

首先描述的是:

算什么。

例如:

text 复制代码
C[i,j] = Σ A[i,k] × B[k,j]

但是你还没有告诉编译器:

怎么算。

例如可以采用:

text 复制代码
Tile = 16 × 16

也可以:

text 复制代码
Tile = 32 × 32

甚至:

text 复制代码
128 × 128

还可以进一步决定:

text 复制代码
Thread Block 数量
Warp 数量
Shared Memory 大小
Register Tile
Vectorize
Unroll
Pipeline

所以 TVM 把问题拆成了:

text 复制代码
Compute
   │
   │  描述数学计算
   ▼
Schedule
   │
   │  描述如何映射到硬件
   ▼
Generated Kernel

TensorIR 的 Schedule 就承担了大量这样的变换,例如 tiling、vectorization 和 thread binding;TVM 还提供 MetaSchedule 等机制搜索性能更好的调度。(Apache TVM)


四、为什么 Tiling 如此重要?

假设:

text 复制代码
A = 4096 × 4096
B = 4096 × 4096

计算:

text 复制代码
C = A × B

显然不可能把整个矩阵一次放进 GPU 的 Shared Memory。

于是需要切块。

例如:

text 复制代码
A

┌──────┬──────┬──────┐
│Tile  │Tile  │Tile  │
├──────┼──────┼──────┤
│Tile  │Tile  │Tile  │
├──────┼──────┼──────┤
│Tile  │Tile  │Tile  │
└──────┴──────┴──────┘

假设每个 Block 计算:

text 复制代码
128 × 128

大小的 C。

那么:

text 复制代码
C Tile
128 × 128

对应需要不断加载:

text 复制代码
A Tile:128 × 32
B Tile:32 × 128

于是 GEMM 变成:

text 复制代码
for k = 0 ... K step 32:

    Global Memory
          │
          ▼
    A[128 × 32]
    B[32 × 128]
          │
          ▼
    Shared Memory
          │
          ▼
     Tensor Core
          │
          ▼
      Register
          │
          ▼
    C[128 × 128]

这里的 128 × 128 × 32,就是非常典型的 Tile 参数


五、Tile 本质上是在匹配 GPU 的内存层次

GPU 并不是一个单纯的"计算器"。

GPU 性能很大程度取决于:

数据在哪里。

可以粗略画成:

text 复制代码
                 容量大
                   ▲
                   │
              HBM / DRAM
                   │
             Global Memory
                   │
        ─────────────────────
                   │
             Shared Memory
                   │
        ─────────────────────
                   │
               Register
                   │
        ─────────────────────
                   │
      CUDA Core / Tensor Core
                   │
                   ▼
                 计算快

距离计算单元越近:

text 复制代码
延迟通常越低
带宽通常越高
容量通常越小

因此优化 Kernel 的核心经常不是:

text 复制代码
少做一次乘法

而是:

text 复制代码
让数据少去几次 HBM。

例如一个朴素 GEMM 可能重复从 Global Memory 读取 A 和 B。

Tiling 后:

text 复制代码
HBM
 ↓
Shared Memory
 ↓
被很多线程重复使用

一次加载的数据可以参与大量计算。

这就是所谓的:

Data Reuse(数据复用)。


六、问题来了:TVM 为什么还不够?

TVM 非常强大,但对于 Kernel 工程师来说有一个问题:

它的编译器能力很强,但编程模型相对复杂。

为了实现一个高性能 GEMM,你可能需要理解:

text 复制代码
TensorIR
Block
Loop
Schedule
split
reorder
bind
cache_read
cache_write
vectorize
unroll
Tensor Intrinsic

然后进行大量 schedule transformation。

这对于编译器来说非常优雅。

但对于 CUDA Kernel 工程师而言,真正思考的问题往往不是:

text 复制代码
我要如何变换这棵 IR?

而是:

text 复制代码
我的 Block Tile 多大?

A 放 Shared Memory。

B 放 Shared Memory。

C 放 Register。

然后让 load 和 GEMM pipeline 起来。

也就是说,两类人的思维模型存在区别:

text 复制代码
编译器工程师

Loop
 ↓
Split
 ↓
Reorder
 ↓
Bind
 ↓
Transform

而 Kernel 工程师更加习惯:

text 复制代码
Tile
 ↓
Memory
 ↓
Thread / Warp
 ↓
Tensor Core
 ↓
Pipeline

这也是后来很多 GPU DSL 开始强调 Tile Programming Model 的原因。


七、Triton 带来了新的思路

Triton 的重要贡献之一,就是把 GPU Kernel 编程从:

text 复制代码
Thread Programming

提升到:

text 复制代码
Block / Tile Programming

传统 CUDA 中,你经常需要思考:

cpp 复制代码
int tid = threadIdx.x;
int bid = blockIdx.x;

然后:

text 复制代码
thread 0 算什么
thread 1 算什么
thread 2 算什么
...

Triton 则更倾向于让开发者描述:

text 复制代码
这一组线程共同处理什么数据块。

于是编程模型发生了变化:

text 复制代码
CUDA

Thread
Thread
Thread
Thread
   │
   ▼
程序员自己组织
   │
   ▼
Tile


Triton

Tile
 │
 ▼
Compiler
 │
 ▼
Thread / Warp

这极大降低了高性能 GPU Kernel 的开发门槛。


八、那么 TileLang 又是什么?

TileLang 延续了 Tile-based Programming 的思想,但它选择直接建立在 TVM 的编译基础设施之上。

当前 TileLang 官方文档把 Tile 作为编程模型中的一等对象 ,同时允许开发者显式表达 Shared Memory、register fragment、数据搬运、pipeline 等硬件相关行为。程序随后继续下降为 IR,并生成 CUDA、HIP、LLVM 等目标代码。(TileLang)

因此可以这样理解:

text 复制代码
TVM
更像:
Compiler Infrastructure

Triton
更像:
GPU Kernel Programming Language

TileLang
更像:
Tile-based Kernel DSL
        +
TVM Compiler Infrastructure

TileLang 并不是为了"替代 TVM"。

恰恰相反:

TileLang 在很大程度上利用 TVM 作为自己的编译底座。

官方文档也明确说明,TileLang Kernel 可以生成 TIR/TVM IR 函数,其 DSL 进一步经过 lowering 后生成面向具体硬件的代码。(TileLang)


九、TileLang 最重要的变化:Tile 成为一等公民

考虑 GEMM:

C=A\\times B

Kernel 工程师可能首先决定:

text 复制代码
block_M = 128
block_N = 128
block_K = 32

意味着一个 GPU Block:

text 复制代码
计算 C:

128 × 128

每次从 A 加载:

text 复制代码
128 × 32

从 B 加载:

text 复制代码
32 × 128

程序在逻辑上就非常自然:

python 复制代码
A_shared = shared(block_M, block_K)
B_shared = shared(block_K, block_N)

C_local = fragment(block_M, block_N)

for k in pipeline(...):

    copy(A_global_tile, A_shared)
    copy(B_global_tile, B_shared)

    gemm(
        A_shared,
        B_shared,
        C_local
    )

copy(C_local, C_global)

你会发现:

这里已经非常接近 GPU Kernel 工程师脑子里的执行过程。

而不是:

text 复制代码
创建 Loop
 ↓
split loop
 ↓
reorder loop
 ↓
bind thread
 ↓
cache_read
 ↓
compute_at

这就是两种抽象方式最大的区别。


十、TileLang 中的 GPU 内存层次

TileLang 允许开发者非常明确地表达数据应该放在哪里。

例如概念上可以写成:

python 复制代码
A_shared = T.alloc_shared(...)
B_shared = T.alloc_shared(...)

C_local = T.alloc_fragment(...)

其中 alloc_shared 对应 GPU 的片上 Shared Memory,而 fragment 则用于描述更靠近计算单元的寄存器级数据布局;TileLang 还通过 layout inference 将逻辑 Tile 映射到线程和寄存器。(TileLang)

于是:

text 复制代码
A
Global Memory
     │
     │ T.copy
     ▼
A_shared
Shared Memory
     │
     ▼
Tensor Core
     │
     ▼
C_local
Registers
     │
     │ T.copy
     ▼
C
Global Memory

这和真正 GPU 的执行模型高度对应。


十一、为什么 Pipeline 又非常重要?

假设一次 GEMM Tile 包含两个阶段:

text 复制代码
Load Data
Compute

最简单的实现:

text 复制代码
Load 0
Compute 0

Load 1
Compute 1

Load 2
Compute 2

假设:

text 复制代码
Load = 100 ns
Compute = 100 ns

那么三轮:

text 复制代码
200 + 200 + 200
= 600 ns

但 GPU 有异步数据搬运等能力。

于是可以:

text 复制代码
时间 ─────────────────────►

Load0
      Compute0
      Load1
             Compute1
             Load2
                    Compute2

即:

text 复制代码
Load(k+1)

与:

text 复制代码
Compute(k)

重叠执行。

理想情况下:

text 复制代码
100 + 100 + 100 + 100
≈ 400 ns

而不是:

text 复制代码
600 ns

这就是:

Software Pipeline。

TileLang 的 T.Pipelined 等抽象,就是为了让这种 GPU Kernel 模式更加直接地进入 DSL。官方 GEMM 示例同样采用了 shared-memory tile、fragment accumulator、pipelined K-loop 和 T.gemm 的结构。(GitHub)


十二、从代码看 TileLang 的思维方式

一个概念化的 TileLang GEMM 可以写成:

python 复制代码
import tilelang.language as T

@T.prim_func
def matmul(
    A: T.Tensor((M, K), "float16"),
    B: T.Tensor((K, N), "float16"),
    C: T.Tensor((M, N), "float16"),
):

    with T.Kernel(
        T.ceildiv(N, BLOCK_N),
        T.ceildiv(M, BLOCK_M),
        threads=128
    ) as (bx, by):

        A_shared = T.alloc_shared(
            (BLOCK_M, BLOCK_K),
            "float16"
        )

        B_shared = T.alloc_shared(
            (BLOCK_K, BLOCK_N),
            "float16"
        )

        C_local = T.alloc_fragment(
            (BLOCK_M, BLOCK_N),
            "float32"
        )

        T.clear(C_local)

        for k in T.Pipelined(
            T.ceildiv(K, BLOCK_K),
            num_stages=3
        ):

            T.copy(
                A[
                    by * BLOCK_M,
                    k * BLOCK_K
                ],
                A_shared
            )

            T.copy(
                B[
                    k * BLOCK_K,
                    bx * BLOCK_N
                ],
                B_shared
            )

            T.gemm(
                A_shared,
                B_shared,
                C_local
            )

        T.copy(
            C_local,
            C[
                by * BLOCK_M,
                bx * BLOCK_N
            ]
        )

这里只看结构,不纠结具体版本 API。

你会发现整个 Kernel 基本就是:

text 复制代码
申请 Shared Memory
        ↓
申请 Register Fragment
        ↓
Global → Shared
        ↓
Shared → Tensor Core
        ↓
累加到 Register
        ↓
Register → Global

这正是高性能 GEMM 的基本执行框架。


十三、从 TVM 到 TileLang,真正发生了什么变化?

很多人会简单理解为:

text 复制代码
TVM API 太复杂
        ↓
TileLang API 简单一点

其实不够准确。

更重要的变化是:

编程抽象发生了变化。

可以这样看:

技术 核心抽象 开发者主要思考
CUDA Thread 每个线程做什么
TVM TensorIR/Schedule Loop + Block + Transformation 如何变换程序
Triton Block / Tile 一个数据 Tile 怎么计算
TileLang Tile + Memory + Pipeline + Hardware Primitive Tile 如何映射硬件

因此 TileLang 的意义,并不仅仅是:

text 复制代码
少写几行 TVM

而是:

text 复制代码
让 Kernel 的表达方式
更加接近 GPU 的真实执行模型

十四、为什么这种模式非常适合大模型?

过去 CNN 时代,核心算子相对固定:

text 复制代码
Conv
Pooling
BatchNorm
GEMM

厂商库基本可以覆盖:

text 复制代码
cuDNN
cuBLAS

但 LLM 时代出现了大量新的 fused kernel:

text 复制代码
FlashAttention
PagedAttention
RMSNorm
RoPE
SwiGLU
MoE
TopK
Grouped GEMM
Quantized GEMM
FP8 GEMM
Dequant + GEMM
MLA
FlashMLA

这些算子有一个特点:

算法变化非常快。

如果每次都等待:

text 复制代码
CUDA Library
 ↓
Framework
 ↓
正式发布

开发周期可能过长。

于是大模型 Infra 越来越需要:

text 复制代码
Python
  ↓
Kernel DSL
  ↓
快速开发
  ↓
JIT Compile
  ↓
GPU Kernel

这也是 Triton、TileLang 等 DSL 越来越重要的根本原因之一。

TileLang 官方文档目前也已经把 GEMM、FlashMLA 等高性能 AI Kernel 作为典型应用场景。(TileLang)


十五、TVM、Triton、TileLang 到底是什么关系?

可以画成:

text 复制代码
                 深度学习模型
                       │
             PyTorch / Framework
                       │
        ┌──────────────┴──────────────┐
        │                             │
        ▼                             ▼
   图 / 模型编译                 Kernel 开发
        │                             │
        ▼                    ┌────────┴────────┐
       TVM                   │                 │
        │                  Triton          TileLang
        │                    │                 │
    Relax IR                 │             Tile DSL
        │                    │                 │
   TensorIR                  │             TVM IR
        │                    │                 │
        └────────────┐       │        ┌────────┘
                     ▼       ▼        ▼
                    CUDA / LLVM / HIP
                           │
                           ▼
                          GPU

当然现实中的编译栈远比这张图复杂,但它非常适合建立第一层认知。


十六、TVM 并没有"过时"

看到 Triton、TileLang 后,有些人容易产生一个误区:

text 复制代码
既然有 TileLang,
是不是 TVM 不需要了?

其实恰好相反。

TVM 更像:

text 复制代码
Compiler Infrastructure

而 TileLang 更像:

text 复制代码
Kernel Programming DSL

TVM 解决的范围更大:

text 复制代码
Graph IR
Tensor IR
Optimization Pass
Scheduling
Auto Tuning
CodeGen
Runtime
Backend

TileLang 则把注意力集中到:

text 复制代码
高性能 Kernel

当前 TVM 本身仍然同时提供 Relax、TensorIR、Schedule、MetaSchedule、DLight、目标代码生成等组件。(Apache TVM)

因此二者并不是竞争关系,而更接近:

text 复制代码
         TileLang
             │
      Kernel Programming
             │
             ▼
     Compiler Infrastructure
             │
             ▼
            TVM

十七、从 TVM 到 TileLang,其实反映了 AI Compiler 的一个趋势

如果回顾整个过程,会发现一个明显趋势。

早期我们希望:

text 复制代码
模型
 ↓
Compiler
 ↓
自动生成一切

即:

Compiler 尽可能自动决定性能优化。

后来大家发现,对于极致性能 Kernel:

text 复制代码
MatMul
Attention
MoE
Quantization

很多硬件信息必须让专家参与。

于是逐渐变成:

text 复制代码
开发者
   │
   │ Tile / Memory / Pipeline
   ▼
Compiler
   │
   │ Layout / Thread Mapping
   │ CodeGen / Lowering
   ▼
Hardware

也就是说,目标已经不是:

"完全隐藏硬件。"

而是:

隐藏没有必要暴露的硬件细节,同时把影响性能的关键控制权交给程序员。

TileLang 官方设计中的多个编程接口正体现了这一点:它希望同时覆盖更高层的 hardware-unaware 编程、利用 Tile Library 的 hardware-aware 开发,以及更底层的 thread primitives,并允许这些层次混合使用。(TileLang)

这其实是一种非常重要的编译器设计思想:

text 复制代码
Abstraction
≠
把所有硬件隐藏掉

好的 Abstraction
=
只暴露真正重要的硬件信息

十八、一张图理解整个演进

最终可以把这条技术路线压缩成:

text 复制代码
第一阶段
手写 CUDA

Thread / Warp / Shared Memory
全部自己控制

        │
        ▼

第二阶段
TVM

Compute
+
Schedule
+
Compiler Optimization

        │
        ▼

第三阶段
AutoTVM / Ansor / MetaSchedule

自动搜索 Schedule

        │
        ▼

第四阶段
Triton

Block / Tile Programming

        │
        ▼

第五阶段
TileLang

Tile
+
Memory Hierarchy
+
Pipeline
+
Hardware Primitive
+
TVM Compiler Infrastructure

注意,这不是严格的"上一代淘汰下一代"关系。

更准确地说:

深度学习编译器一直在寻找"开发效率、编译自动化和硬件控制能力"之间最合适的平衡点。


十九、学习 TileLang 前应该先掌握什么?

如果想真正理解 TileLang,建议不要一开始就背 API。

应该先理解这条链路:

text 复制代码
矩阵乘法
   ↓
Tiling
   ↓
GPU Thread Block
   ↓
Warp
   ↓
Global Memory
   ↓
Shared Memory
   ↓
Register
   ↓
Tensor Core
   ↓
Double Buffer
   ↓
Software Pipeline

掌握这些以后,再看:

text 复制代码
T.Kernel
T.alloc_shared
T.alloc_fragment
T.copy
T.gemm
T.Pipelined

会发现这些 API 并不是凭空设计出来的。

它们基本都在描述 GPU 中真实存在的东西。


二十、最后总结

如果只记住一句话,可以记成:

TVM 的核心是把张量程序编译到不同硬件,而 TileLang 的核心是让开发者以 Tile 为基本单位,更直接地描述一个高性能 Kernel 应该怎样在硬件上运行。

再进一步:

text 复制代码
TVM
解决:
如何构建 AI Compiler

TileLang
解决:
如何更舒服地写高性能 AI Kernel

TVM 更偏:

text 复制代码
Compiler
IR
Schedule
Transformation
CodeGen

TileLang 更偏:

text 复制代码
Tile
Shared Memory
Register
Tensor Core
Pipeline
Kernel

所以从 TVM 到 TileLang,并不只是:

text 复制代码
语法越来越简单

真正的变化是:

text 复制代码
Tensor Expression
      ↓
Loop / Schedule
      ↓
Tile
      ↓
Memory Hierarchy
      ↓
Hardware Primitive

深度学习编译正在从"让编译器猜怎样跑得快",逐渐发展到"开发者描述关键的高性能计算结构,编译器负责把这些结构可靠、高效地映射到硬件"。

理解这一点,再去学习 FlashAttention、Triton、TileLang、CUTLASS,甚至各种大模型推理框架中的自定义 Kernel,就会发现它们背后的很多设计其实是一脉相承的。

相关推荐
海天一色y7 小时前
GSPO:重新定义大语言模型的强化学习训练范式
人工智能·机器学习·语言模型
云和数据.ChenGuang7 小时前
fastapi项目拆分实战数据模型
java·服务器·数据库·人工智能·深度学习·fastapi·强化学习
watersink7 小时前
机器学习HMM
人工智能·机器学习
东方小月7 小时前
从零开发一个 Coding Agent(九):实现 Agent 的工具调用闭环
人工智能·前端框架·node.js
Luhui Dev7 小时前
如何在 WorkBuddy 中使用大角几何:从 MCP 接入到 AI 几何作图
人工智能·数学·算法·agent·luhuidev
孙启超7 小时前
【AI应用开发】什么是混合检索(Hybrid Search)?向量检索 + BM25 关键词检索,适用场景与 RRF 融合原理
人工智能·缓存·llm·向量数据库·bm25·向量化·ai应用开发
The moon forgets7 小时前
Qwen团队提出Ego2Robot, 第一人称视频助力具身VLA训练新数据
人工智能·机器学习·音视频
szxinmai主板定制专家8 小时前
基于 RK3588 + Xilinx Kintex-7 FPGA 异构工业主控板设计方案
arm开发·人工智能·嵌入式硬件·fpga开发·zynq