vivo AI Infra 校招
Q: TVM中图优化和算子优化分别做什么?设备如何提供支持?
图优化(Relay级 / Graph-level):
操作对象是计算图中的算子节点和边,关注算子间的关系:
| 优化类型 | 说明 | 效果 |
|---|---|---|
| 算子融合(Fusion) | 将多个小算子合并为一个kernel(如Conv+BN+ReLU) | 消除中间tensor的HBM读写,减少kernel launch开销 |
| 常量折叠(Constant Folding) | 编译期计算常量表达式 | 减少运行时计算 |
| 死代码消除 | 移除不影响输出的计算 | 减少无效计算 |
| 布局转换(Layout Transform) | NCHW→NHWC或其他设备友好的数据布局 | 匹配硬件最优访存模式 |
| 形状推断与静态化 | 将动态shape尽可能静态化 | 使后续优化可应用 |
设备对图优化的支持:通过target描述提供硬件信息(如”cuda -arch=sm_80”),告知编译器可用的融合规则(如Tensor Core要求的shape约束)、最优数据布局等。
算子优化(TIR级 / Operator-level):
操作对象是单个算子内部的循环结构和内存访问:
| 调度原语 | 说明 | 目标 |
|---|---|---|
| split/tile | 循环分块 | 适配缓存层次(寄存器→SMEM→L2→HBM) |
| reorder | 改变循环嵌套顺序 | 提高数据局部性 |
| vectorize | 向量化内层循环 | 利用SIMD/向量加载指令 |
| unroll | 循环展开 | 减少循环开销,暴露ILP |
| parallel | 标记可并行维度 | 映射到多核/多线程 |
| bind | 将循环绑定到GPU线程层次 | blockIdx/threadIdx映射 |
| cache_read/write | 数据缓存到更快层次 | 利用共享内存/寄存器 |
| compute_at | 控制计算嵌入位置 | 减少中间buffer大小 |
设备对算子优化的支持:通过硬件参数指导调度决策——SM数量(决定grid大小)、每SM共享内存大小(48KB-164KB,决定tile大小)、寄存器数量(64K/SM,决定每线程的数据重用量)、向量宽度(float4=16B)、Warp大小(32)等。
两级优化的协作关系:
1 | 模型 → [Relay图优化: 融合+布局] → 优化后的子图 → [TIR算子优化: tiling+调度] → 高性能kernel → 目标代码 |
图优化减少算子数量和中间数据,算子优化使每个算子内部执行效率最大化。
Q: TVM的Auto-Tuning方案设计是什么?与手动优化的区别和优劣?
Auto-Tuning的完整流程:
1 | 1. 定义搜索空间(Schedule Template) |
与手动优化的对比:
| 维度 | Auto-Tuning | 手动优化(CUDA专家) |
|---|---|---|
| 适配性 | 自动适配不同shape/硬件 | 特定shape/硬件极致优化 |
| 开发效率 | 定义搜索空间后自动搜索 | 需要深厚的硬件知识 |
| 性能上限 | 受限于搜索空间定义 | 可使用任意优化技巧 |
| 搜索时间 | 数小时~数天 | 人工数天~数周 |
| 可迁移性 | 换硬件只需重新tuning | 需要重新适配 |
| 可维护性 | 代码自动生成,修改困难 | 代码可读可维护 |
| 复杂优化 | Warp Specialization等难以表达 | 无限制 |
AutoTVM vs Ansor vs 手动优化的性能对比(以GEMM为例):
- cuBLAS (NVIDIA手动优化): 100% 性能基线
- Ansor: 通常达到cuBLAS的80-95%
- AutoTVM (需要手写template): 达到70-90%
- 未优化的naive实现: 5-15%
适用场景建议:
- Auto-Tuning最适合:大量不同shape的算子、非NVIDIA硬件(无高质量库)、快速原型验证
- 手动优化最适合:核心热点算子(如FlashAttention)、需要极致性能、复杂的优化模式(如persistent kernel、warp specialization)
- 最佳实践:对标准算子用Auto-Tuning快速达到80%性能,对关键算子手动优化追求最后的20%
Q: 其他编译框架有哪些?对比TVM有何不同?
主流编译框架全景对比:
XLA(Accelerated Linear Algebra, Google):
- 定位:面向TPU/GPU的图编译器,与TensorFlow/JAX深度绑定
- 核心特点:
- 自动算子融合能力强(HLO IR上的fusion pass)
- 对TPU优化极致(毕竟同源于Google)
- 支持自动分布式并行(GSPMD)
- vs TVM:XLA是封闭生态(TF/JAX only),TVM支持任意前端;XLA对NVIDIA GPU的优化不如TVM/Triton灵活
- 适用场景:Google Cloud TPU上的训练/推理
Triton(OpenAI):
- 定位:GPU kernel编程DSL,介于CUDA和Python之间的抽象层
- 核心特点:
- 用Python-like语法写kernel,自动处理tiling、内存管理、并行映射
- 程序员只需指定tile大小和内存访问模式,编译器自动生成高效CUDA代码
- 编译到LLVM IR再到PTX
- vs TVM:Triton是kernel-level DSL(手写单个kernel),TVM是端到端编译器(从模型到部署)。Triton给程序员更多控制权但需要理解GPU编程概念
- 性能:手写Triton kernel可达cuBLAS 90-100%性能,开发效率是CUDA的5-10倍
- 适用场景:快速开发自定义kernel(如FlashAttention最初用Triton实现原型)
MLIR(Multi-Level Intermediate Representation, Google):
- 定位:通用的编译器基础设施框架,提供可扩展的方言(Dialect)机制
- 核心特点:
- 多层IR支持渐进式lowering:High-level Dialect → Mid-level → Low-level → LLVM
- 可定义领域特定的Dialect(如Linalg for 线性代数、GPU for GPU操作)
- 强大的pattern rewrite和pass infrastructure
- vs TVM:MLIR是构建编译器的框架的框架,TVM是完整的端到端DL编译器。用MLIR可以从零构建类似TVM的编译器,但需要大量开发工作
- 适用场景:构建新的领域编译器、硬件厂商的编译栈
TensorRT(NVIDIA):
- 定位:闭源推理优化器,对NVIDIA GPU极致优化
- 核心特点:
- 自动选择最优kernel实现(从预编译的kernel库中选择)
- 支持INT8量化(需要校准)、FP16、结构化稀疏
- Layer fusion、kernel auto-tuning
- 支持动态shape(dynamic shape profile)
- vs TVM:TensorRT对NVIDIA硬件优化通常优于TVM(闭源手调kernel),但完全不跨平台。TVM开源可扩展但NVIDIA上性能稍逊
- 典型加速:对推理场景,TensorRT通常比PyTorch eager快3-10倍
- 适用场景:NVIDIA GPU上的生产推理部署
Halide:
- 定位:计算与调度分离的先驱(2012年MIT)
- 核心贡献:提出 Algorithm(计算逻辑)和 Schedule(执行策略)分离的编程模型——TVM的Schedule设计直接受其启发
- vs TVM:Halide主要面向图像处理pipeline(2D stencil),TVM扩展到通用张量计算和深度学习
- 适用场景:图像处理、计算摄影
框架选择决策树:
1 | 需要部署推理? |