The Evolution and Applications of CuTeDSL

Jie Fang (方杰) NVIDIA GPU加速计算专家团队、高级工程师
Yuxi Chi (池宇希) NVIDIA GPU加速计算专家团队、高级工程师

议程 (Agenda)

为何选择 CuTeDSL

CUTLASS C++ 概述

CUTLASS (CUDA Templates for Linear Algebra Subroutines) 是一个高性能的 CUDA C++ 模板库,它提供了一套用于生产力和性能的抽象,适用于所有范围和规模。该项目是开源的,可在 https://github.com/NVIDIA/cutlass 获取。

其架构分层,从提供更多预调优方案(recipes)的上层到底层提供更多控制(control)的原子操作。

Page 4, CUTLASS C++ 架构分层图
Page 4, CUTLASS C++ 架构分层图

使用 C++ 的主要痛点

这些问题引出了一个核心疑问:我们是否必须忍受 C++ 的这些不便?

CuTe DSL 的关键优势

显著更快的编译速度

与 C++ 相比,CuTeDSL 的编译速度显著提升了30至100倍。下图展示了在 Blackwell 架构上,Cutlass C++ 与 CuTe DSL 在不同应用场景下的编译时间对比。

Page 7, Cutlass C++ 与 CuTe DSL 在 Blackwell Kernels 上的编译时间对比
Page 7, Cutlass C++ 与 CuTe DSL 在 Blackwell Kernels 上的编译时间对比

与 C++ 性能持平

尽管编译速度大幅提升,CuTeDSL 在数学计算效率上仍能与 C++ 实现相媲美。

下图比较了在 Blackwell 架构上,不同问题规模下 C++ 和 DSL 的数学计算效率(Math Efficiency %)。数据显示,在稠密GEMM、分组GEMM以及Flash Attention等多种场景下,DSL 的性能与 C++ 基本持平,甚至在某些情况下略有优势。

Page 8, C++ 与 DSL 在 Blackwell 架构上的性能对比
Page 8, C++ 与 DSL 在 Blackwell 架构上的性能对比

CuTeDSL 在不同工具中的定位

CuTeDSL 在生产力(productivity)和性能(performance)之间取得了独特的平衡。与 Torch 和 Triton 等更高层次的抽象相比,CuTeDSL 提供了接近 CUDA/PTX 的极致性能,同时保持了比直接编写底层代码更高的生产力。

Page 9, 不同工具在生产力与性能上的权衡
Page 9, 不同工具在生产力与性能上的权衡

下表总结了不同工具的性能和上手时间:

工具 性能 (内存密集型) 性能 (计算密集型) 上手时间
Torch compile ~90% ~70-80% 小时-天 (hours-days)
Triton ~90% ~80-90% 天-周 (days-weeks)
Cute-DSL 100% 100% 周-月 (weeks-months)

该图表引用自 tri dao 的幻灯片。

特性 (Features)

支持 DLPack 协议

CuTeDSL 通过支持 DLPack 协议,能够与 PyTorch 等深度学习框架无缝集成。它支持从 torch tensor 的隐式转换,并提供了 mark_layout_dynamicmark_compact_shape_dynamic 等接口来处理动态的布局和形状。

Page 10, DLPack 协议支持代码示例
Page 10, DLPack 协议支持代码示例

即时编译缓存 (JIT Caching)

CuTeDSL 支持 JIT 编译结果的缓存,允许用户定义自己的缓存机制,从而避免重复编译,进一步加快开发和执行速度。

Page 11, JIT 缓存代码示例
Page 11, JIT 缓存代码示例

打印与调试 (Print)

CuTeDSL 提供了强大的打印功能,用于调试。与 Python 的 print 函数在编译时打印静态值不同,cute.printf 可以在运行时同时显示静态和动态值,这对于调试 GPU kernel 至关重要。

Page 12, 打印功能代码示例
Page 12, 打印功能代码示例

软件流水线 (Software pipeline)

CuTeDSL 的编译器能够生成高效的软件流水线代码,其性能与专家手写的版本相当。下图比较了在不同问题规模和分块配置下,手写代码与编译器生成的代码在数学计算效率上的表现,两者几乎没有差异。

Page 13, 手写与编译器生成的软件流水线性能对比
Page 13, 手写与编译器生成的软件流水线性能对比

更新 (Updates)

示例的演进

从最初发布的12个示例,CuTeDSL 已经扩展到最新的31个示例。示例库的增长反映了其功能的丰富和对更多新架构(如 Blackwell, Hopper)和新模型(如 Mamba2, Flash Attention)的支持。

Page 14, CuTeDSL 示例从初始发布到最新的对比
Page 14, CuTeDSL 示例从初始发布到最新的对比

最新更新摘要

CuTeDSL 的应用

用户案例

CuTeDSL 已被业界广泛采用:

Page 16
Page 16

flash-attention 4

下图展示了 FlashAttention 4 在 Blackwell 架构上的性能。在 B200 GPU 上,对于前向传播(causal=True, headdim=128),FA4 的 TFLOPS 性能显著优于 cuDNN,尤其是在序列长度较长时。

Page 17
Page 17

QuACK 内存密集型案例

CuTeDSL 使客户和框架能够轻松采用和自定义其他内核。下图展示了 QuACK 在内存密集型操作(RMSNorm, Softmax, Cross-Entropy)上的性能,并与 torch.compile、Liger Kernel 和 cuDNN 进行了比较。QuACK (基于 CuTe DSL 构建) 在 RMSNorm 和 Softmax 上表现出优越的性能。

Page 18
Page 18

路线图 (Roadmap)

未来的发展计划包括:
- 更多生产就绪的示例:
- Blackwell GEMM + COMM via NVSHMEM API
- Blackwell FP16/FP8 稀疏 GEMM

Page 19
Page 19

CuTeDSL 作为 Tilelang 的代码生成目标 (CodeGen Target)

基于 Tile 的 DSL 概述

编程模型抽象

基于 Tile 的 DSL 采用分层编程模型来分解计算任务:
- Grid level (网格层): 代表整个工作负载。用户仅控制网格级别的操作。
- Block level (块层): 网格被划分为多个块(Tiles)。用户需要控制任务的划分以及对 Tiles 的操作。
- Thread level (线程层): 块被映射到线程。用户需要控制任务划分、线程映射以及线程级别的操作。

Page 21
Page 21

OpenAI Triton 与 Tilelang 对比

下表比较了 OpenAI Triton、Tilelang 和 CuTeDSL 在不同抽象层次上的控制粒度。CuTeDSL 提供了最显式和手动的底层控制,而 Triton 提供了最高层次的自动化。

特性 OpenAI Triton Tilelang CuTeDSL
内存分配 自动 自动 T.alloc_xxx GMEM, SMEM, TMEM, RF
内存布局 自动 自动 T.annotate_layout cute.Layout
内存移动 自动 T.Copy cute.CopyAtom
计算策略 自动 T.Gemm cute.MmaAtom
流水线 stages stages 手动
Page 22
Page 22

Tilelang/TIR 作为原生代码生成器

Tilelang 作为原生代码生成器

Tilelang 是一个硬件无关的张量中间表示(Tensor Intermediate Representation, TIR),具有以下特点:
- 显式控制: 允许对内存分配和内存移动进行显式控制。
- Tile 索引: 提供在 Tile 内进行索引的能力。
- 代码注入: 支持 C++ 和 PTX 指令注入。
- 原生代码输出:
- 适用于 NVIDIA GPUs (Volta, Ampere, Hopper 等) 的 CUDA C++
- 适用于 AMD GPUs (MI300, MI250 等) 的 HIP C++
- 适用于 CPUs 的 C++/llvm
- 适用于其他加速器的原生代码

下图展示了从初学者到专家的编程抽象层次,从高层的 Tile 程序到底层针对特定硬件(NVIDIA/AMD GPUs)的可执行文件。

Page 23
Page 23

Tilelang 到 TIR 的降级 (Lowering)

Tilelang 提供了高层原语(如 T.alloc_shared, T.gemm),通过 tilelang.compile 将高级 Tilelang 代码降级为更详细的 TIR 代码。TIR 包含了如 T.vectorized, T.unroll 等更底层的原语。

Page 24
Page 24

以下是针对不同硬件架构的 TIR 代码生成示例:

Page 25
Page 25
Page 26
Page 26

CUDA 代码生成: 从 TIR 到 CUDA

TIR 原语最终会被重写为 CUDA 代码。
- 内存分配和 Mbarrier 初始化: TIR 中的内存分配和屏障创建原语会映射到 CUDA 中的 __shared__ 内存声明和 mbarrier 初始化函数。
- CUDA API 包装器: https://github.com/tile-ai/tilelang/tree/main/src/tir/templates/cuda

Page 27
Page 27
Page 28
Page 28

CuTeDSL 作为代码生成目标

Tilelang CuTeDSL 后端

Tilelang 使用 CuTeDSL 作为后端,以生成高效的设备代码。该后端的功能分解如下:
- Python 代码生成 (Host code):
- 生成主机端代码,包括 JIT 包装器、内核启动配置(grid, block, smem)和内核调用。
- 管理 TMA 描述符。

下图展示了 Python 代码如何调用后端,将 PyTorch 张量转换为 CuTe 张量,并启动编译后的 CUDA 内核。

Page 29
Page 29

示例:逐元素加法

下面是一个将 Tilelang 代码转换为 CuTeDSL 代码的具体示例:

Page 30
Page 30

示例:Ampere GEMM

此页面展示了如何使用 TileLang 编写一个通用的矩阵乘法(GEMM)内核,并将其编译为针对 NVIDIA Ampere 架构优化的 CuTeDSL 代码。

Page 31
Page 31

示例:Hopper fp8 GEMM

此页面展示了针对 NVIDIA Hopper 架构的 FP8 GEMM 的一个更复杂的示例。

Page 32
Page 32

CuTeDSL 后端 vs CUDA 后端

此页面通过实验数据对比了 CuTeDSL 后端和传统的 CUDA 后端在编译时间和内核运行时间上的表现。

Page 33
Page 33

性能对比表

示例 后端 首次编译时间 内核运行时间
Elementwise Add CUDA 11.81s 9.622 ± 0.038 µs
CuTeDSL 0.68s 9.974 ± 0.040 µs
Ampere Gemm Float16
M=N=K=8192
CUDA 15.43s 6.042 ± 0.234 ms
CuTeDSL 0.59s 5.498 ± 0.260 ms
Hopper Gemm Float16
M=N=K=8192
CUDA 15.38s 3.006 ± 0.035 ms
CuTeDSL 0.66s 3.034 ± 0.032 ms

CuTeDSL 作为代码生成目标:总结

本页总结了将 CuTeDSL 作为 TileLang 代码生成目标的优势。


Page 35
Page 35