Triton/TileLang Tile-Level Programming
Why we need tile-level programming 为什么需要分块级编程
在最普通的SIMT中,我们会遇到线程协作、数据复用、shared memory这些内容都需要手动管理,增大了编程难度;且写起来繁琐而易错,优化比较困难.为了摆脱这种困境,Tile-Level是我们的首选.它把block内线程协作抽象为分块(tile)操作,相关工具负责把tile操作映射到具体的线程,使程序员不需要详细思考操作维护底层线程的逻辑,简化了实现难度.
在Tile-Level编程中,我们通常需要遵循下面的步骤或思路:
- 确定当前tile的位置
- 从global memory加载tile
- 对tile进行计算
- 将结果写回global memory
- 处理边界(不一定最后,根据实际处理)
而为了实现这一过程,我们采用Triton/TileLang工具,作为python的两种不同的扩展语言,它们是以上思想的两种DSL(Domain-Specific Language)表达.
Triton模型
Triton与CUDA主要有以下的区别:
CUDA
- CUDA Kernal从thread级别出发,显式地分配计算每个线程负责的数据
Triton Kernal
- Triton Kernal从program出发,每个program负责一个数据tile
- 程序员描述tile的位置、形状以及load/compute/stroe操作
- 编译器负责将tile内的计算映射到thread和warp
以下给出一个计算向量加法的kernal示例:
1 | #引入相关模块 |
类似地,得到矩阵分块乘法的代码:
1 | import torch |
所以,对于上述代码,只要你读了,就可以看到Triton的局限:
- 编译器是黑盒,内部全靠编译器
- 生成的PTX/SASS不透明,性能问题难以定位
- 复杂的pipeline或warp specialization难表达
- 复杂kernal写起来别扭
- 报错和性能回退难分析
因此,我们需要TileLang
TileLang Programming Model TileLang编程模型
TileLang更强调显示写出层次结构,拥有GPU kernal的同时,可以输出显存、共享内存、CTA内共享缓存块等,也可以沿着某维度分块循环,更接近于手写的CUDA kernal结构
以下是一个示例图:
右侧补全的完整矩阵乘法代码贴在这里:
1 | import tilelang |
Conclusion 小结
对于Triton,TileLang,以及传统CUDA,三者有着明显不同的应用场景以及优势: 1. Triton适合快速简洁的效率场景,尤其是不关注底层以及加速的算法. 2. TileLang则适合既需要一定简洁性,又能够表达出一定底层内存层次和数据情况的场景,易于讲清楚高性能kernal结构. 3. CUDA的控制粒度最细,深入底层结构,对线程级别规划以及内存控制最为直观,适合针对底层的编写或者优化场景,对于基于硬件结构的极端性能调控或者特殊硬件机制研究有无可比拟的绝对优势,但代价是代码比较繁复,维护理解较困难.
擅长python语言或有python对GPU编程基础的读者,对前两种可能更容易掌握;对于CUDA,则更适合于C/C++的编程者.







