CUTLASS / CuTe 主线

8 课现代 GPU 模板库与 Layout 代数

CUTLASS / CuTe 核心主线

本主线深入 NVIDIA CUTLASS 3.x 与核心抽象 CuTe,学习 GPU 硬件层级演进、Tensor Core 算术强度、CuTe Layout 代数与分层 GEMM 实现。

学习目标

  1. 理解 Volta → Ampere → Hopper → Blackwell 的 Tensor Core 演进;

  2. 掌握 CuTe Layout 核心代数:Shape、Stride、坐标变换与切片;

  3. 掌握 Tiled Copy 与 Tiled MMA 的构建与使用;

  4. 理解异步数据加载(cp.async)、Shared Memory 多级流水与 Epilogue 融合。


课程目录

课程名称 模块归属
第 1 课:GPU 层级、Tensor Core 与算术强度 CUTLASS / CuTe 主线
第 2 课:CuTe Layout:Shape、Stride 与坐标映射 CUTLASS / CuTe 主线
第 3 课:CuTe Tensor、视图与线程分区 CUTLASS / CuTe 主线
第 4 课:Tiled Copy、向量化与 Predication CUTLASS / CuTe 主线
第 5 课:MMA Atom、Tiled MMA 与三级 Tiling CUTLASS / CuTe 主线
第 6 课:Shared Memory 布局与异步流水 CUTLASS / CuTe 主线
第 7 课:混合精度、Accumulator 与 Epilogue CUTLASS / CuTe 主线
第 8 课:Kernel 组合、Dispatch 与 Profiling CUTLASS / CuTe 主线

核心架构图解

CUTLASS 教程:从手写 GEMM 到 CuTe / Tensor Core Kernel
学习目标:看懂 CUTLASS 的层级抽象,能把 ThreadblockShape / WarpShape / InstructionShape 和数据搬运、pipeline、epilogue 对上
一、先把 GEMM 三重循环映射到 GPU 层级
数学 GEMM
C[M,N] = A[M,K] x B[K,N]
CTA / Threadblock Tile
ThreadblockShape{kM,kN,kK}
一个 block 负责一个 C tile
Warp Tile
多个 warp 分摊 CTA tile
从 smem 读到寄存器
MMA Atom
mma.sync / wgmma
Tensor Core 指令
Epilogue
alpha/beta
bias/activation/store
二、CUTLASS 2.x / 传统 GEMM API 怎么读
device::Gemm
host 侧入口,语义接近 cuBLAS
threadblock::MmaPipelined
global -> shared -> register
K 维 mainloop
warp::Mma
warp 级 outer-product 累加
Iterator / Layout
描述 tile 访问、边界 predication、访存布局
读法:先定位 device API,再追 mainloop,最后看 iterator 如何把矩阵 tile 化。面试重点是 tile 层级、共享内存复用、bank conflict、occupancy 与 pipeline stage。
三、CUTLASS 3.x / CuTe 怎么理解
Collective Mainloop
GmemTiledCopy + SmemLayout
TiledMma / pipeline
Collective Epilogue
accumulator -> output tile
融合 scale/bias/activation
CuTe Layout Algebra
Shape / Stride / Layout
把索引映射写成类型
Atom / Tiled MMA Copy
MMA 原子操作 + 分块组合
Hopper/Blackwell 可接 TMA/WGMMA
读法:CuTe 的核心不是新数学,而是用 Layout/Tile/Atom 精确表达数据在哪、怎么搬、由哪个 MMA 原子消费。先学 layout,再学 tensor,再看 GEMM tutorial。
四、面试可讲的优化主线
Global Memory
coalesced load
A/B tile 从 gmem 搬入 smem
Shared Memory
数据复用
bank conflict free layout
Register Accumulator
每线程/warp 保持 C fragment
提升 ILP
Pipeline Stage
cp.async / TMA 预取
隐藏 gmem latency
Epilogue Fusion
store 前融合
减少额外 kernel
必会问题 1
ThreadblockShape 变大为什么可能更快,也可能 occupancy 下降
必会问题 2
Warp tile / instruction tile 分别对应什么硬件层级
必会问题 3
为什么需要 epilogue,bias/activation 放哪里融合
练习路线
naive GEMM -> tiled GEMM -> WMMA -> CUTLASS profiler -> CuTe layout

CUTLASS 教程:GEMM 到 CuTe 原理 · Excalidraw 源文件

ldmatrix 指令与 CuTe TV Layout 映射
核心:搞清 (Thread, Value) → (Row, Col) 的映射,以及 ldmatrix 为什么能把 SMEM 数据直接放成 mma.sync 期望的寄存器格式。
Part 1:ldmatrix.m8n8.x1,一个 8×8 fp16 矩阵
谁提供地址
T0–T7 各提供一行的 SMEM 地址(每行 16 B = 8 个 fp16)。
T8–T31 的地址在 .x1 中不使用,但 32 个线程都必须执行这条 .sync.aligned 指令。
谁拿到数据
row = lane / 4 col_start = (lane % 4) * 2 reg (u32) = { M[row][col_start], M[row][col_start+1] }
每 4 个线程分一行,每线程拿 2 个相邻 fp16。
线程组 lane%4 = 0 1 2 3
T0–T3 T0: (0,0) (0,1) T1: (0,2) (0,3) T2: (0,4) (0,5) T3: (0,6) (0,7)
T4–T7 T4: (1,0) (1,1) T5: (1,2) (1,3) T6: (1,4) (1,5) T7: (1,6) (1,7)
T8–T11 T8: (2,0) (2,1) T9: (2,2) (2,3) T10: (2,4) (2,5) T11: (2,6) (2,7)
… … … … …
T28–T31 T28: (7,0) (7,1) T29: (7,2) (7,3) T30: (7,4) (7,5) T31: (7,6) (7,7)
Part 2:ldmatrix.x4,一次加载 16×16 的 A 操作数
矩阵 地址来自 覆盖区域 写入寄存器 对应 mma A 片段
Mat 0 T0–T7 M 0–7,K 0–7 d0 a0, a1
Mat 1 T8–T15 M 8–15,K 0–7 d1 a2, a3
Mat 2 T16–T23 M 0–7,K 8–15 d2 a4, a5
Mat 3 T24–T31 M 8–15,K 8–15 d3 a6, a7
地址按这个顺序给,d0–d3 就正好是 mma.m16n8k16 的 A 片段顺序。线程 t 的 4 个寄存器:
g = t / 4, c = (t % 4) * 2 d0 = {A[g][c], A[g][c+1]} d1 = {A[g+8][c], A[g+8][c+1]} d2 = {A[g][c+8], A[g][c+9]} d3 = {A[g+8][c+8], A[g+8][c+9]} 例 t = 5: g = 1, c = 2 → d0 = A[1][2..3], d1 = A[9][2..3], d2 = A[1][10..11], d3 = A[9][10..11]
Part 3:CuTe 里的 SM80_16x8x16 ALayout
ALayout: (T32, V8) → (M16, K16),按列主序 idx = m + 16·k Shape <Shape <_4, _8>, Shape <_2, _2, _2>> Stride<Stride<_32,_1>, Stride<_16,_8,_128>>
模式 大小 stride 含义
线程 t % 4 4 32 K 方向 +2 列
线程 t / 4 8 1 M 方向 +1 行
值 v0 2 16 K 方向 +1 列(同一寄存器里的两个 fp16)
值 v1 2 8 M 方向 +8 行(下半块)
值 v2 2 128 K 方向 +8 列(右半块)
Thread 0 持有 (0,0) (0,1) | (8,0) (8,1) | (0,8) (0,9) | (8,8) (8,9),与 Part 2 的 d0–d3 一致。
make_tiled_copy_A(Copy_Atom<LDSM>{}, tiled_mma) 会按 MMA 的 ALayout 反推每个线程该拷哪些元素,所以 ldmatrix 的输出可以直接喂给 mma.sync。K-major 的 A 用 SM75_U32x4_LDSM_N(ldmatrix.x4);M-major 的 tile 才用 SM75_U16x8_LDSM_T(.x4.trans)。
Part 4:全流程 GMEM → SMEM → Register → MMA
Global Memory
A[M×K]、B[N×K]
cp.async:SM80_CP_ASYNC_CACHEGLOBAL<uint128_t>
Shared Memory
SmemLayoutAtom + Swizzle<3,3,3>
把行号 XOR 进 16 B 块索引,ldmatrix 读的 8 行落在不同 bank,无冲突。
Register
ldmatrix 写成 MMA 格式:A 每线程 4 个 u32(8 fp16),B 每线程 2 个 u32(4 fp16)。
Accumulator
mma.sync.m16n8k16:C/D 每线程 4 个 f32,(T32, V4) → (M16, N8)。
using MMA = TiledMMA<MMA_Atom<SM80_16x8x16_F32F16F16F32_TN>>; auto copy_A = make_tiled_copy_A(Copy_Atom<SM75_U32x4_LDSM_N, half_t>{}, mma); auto copy_B = make_tiled_copy_B(Copy_Atom<SM75_U32x4_LDSM_N, half_t>{}, mma); cute::copy(copy_A, tCsA, tCrA_view); // 发射 ldmatrix cute::gemm(mma, tCrA, tCrB, tCrC); // 发射 mma.sync

ldmatrix TV Layout 详解 · Excalidraw 源文件