← 返回目录

下面是一份"照着读就能写"的清单,全部基于仓库实际行号(未改动任何文件)。


一、PYBIND 到底写在哪、怎么写

1. kernels/common/pack.cuh(30 行,整个仓库的绑定"语法糖"只有一行)

2. kernels/hgemm/hgemm.cu:884-934(这是唯一需要抄的"顶层实验接口"C++ 侧)

884  #define CHECK_T(x) TORCH_CHECK(x.is_cuda() && x.is_contiguous(), ...)
886  #define binding_tiled_func_gen(name)  ...        # 生成 host 侧 launcher
919-922  binding_tiled_func_gen(hgemm_naive / bcf / bcf_dbf / bcf_dbf_rw);
924  extern void hgemm_cublas(torch::Tensor, torch::Tensor, torch::Tensor);
928  PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { torch_pybinding_func(...); }

binding_tiled_func_gen 宏体(886-917 行)就是你的新 kernel 与 Python 之间唯一的契约,逐行看它做了什么:

  1. CHECK_T(a/b/c) → 要求 cuda + contiguous(你的 A2/A2L 传 b_t 也要 contiguous,host 侧 .t().contiguous() 即可)。
  2. M=a.size(0), K=a.size(1), N=b.size(1) → 注意 B 的形状约定是 [K, N];A2/A2L 的 B 是 [N, K],所以 launcher 里 N/K 的取法要变(N=b.size(0), K=b.size(1)),别照抄。
  3. BM=BN=128, threads=256, dim3 blocks_per_grid((N+BN-1)/BN, (M+BM-1)/BM) → grid 顺序是 x=N 方向、y=M 方向。
  4. stream = at::cuda::getCurrentCUDAStream() → 必须在当前 stream 上跑。
  5. if (a.dtype()==torch::kHalf) ... else ... → fp16/bf16 分派。
  6. name##_kernel<128,128,32><<<grid, 256, 0, stream>>>(...) → 模板实参在这里写死。

⚠️ 你要做 STAGES=3(48KB dynamic smem)时,这段宏不够用:还要在 launch 前加 cudaFuncSetAttribute(kernel_ptr, cudaFuncAttributeMaxDynamicSharedMemorySize, bytes) 并把 smem 大小作为 <<<...>>> 第 3 个参数传入。 现成写法参考(虽然那两个文件是 TMA、SM80 不可用,但只抄这段 opt-in 写法即可):

3. kernels/hgemm/cublas.cu("多 .cu 编成一个模块"的写法)

4. kernels/sgemm/sgemm.cu:526-541(另一个 PYBIND 例子,确认"一函数一名字"的对应关系)


二、.py 顶层实验接口怎么写

1. kernels/hgemm/test.py(这就是你要模仿的模板,逐块看)

行号 内容 你要拿走什么
7-9 from torch.utils.cpp_extension import load / torch.set_grad_enabled(False) 编译入口、禁梯度
11-30 load(name="hgemm_lib", sources=[...], extra_cuda_cflags=[...], extra_cflags=common_flags, verbose=True) 唯一的编译+加载点:sources 里放你新写的 .cu;extra_cuda_cflags 里的 -U__CUDA_NO_BFLOAT16_CONVERSIONS__、--expt-relaxed-constexpr、--use_fast_math、-Xptxas -v 必须保留(最后一个会打印每个模板实例的 smem/寄存器用量,是文档 §7.4 的验证手段)
33-65 benchmark(op, a, b, c=None, warmup=10, rep=200, prefix=...) 计时函数:warmup → torch.cuda.synchronize() → perf_counter() → rep 次 → sync;TFLOPS = M*N*K*2*rep/1e12/duration;prefix=="torch" 时把结果存为 baseline,其余算 speedup。A1/A2/A2L/A3 的表格就靠这个函数 + 不同 prefix 输出
67-73 diff_check(a, b, prefix, eps=0.016) bf16 容差 0.016;A1~A3 之间应 torch.equal 级别为 0
76-104 test_all()(多尺寸扫描,被注释掉) 想扫 256/512/1024/4096/8192 时取消注释
107-155 test_4096() 你的主实验入口:造 a/b/c → torch.matmul baseline → lib.hgemm_cublas → lib.hgemm_naive/bcf/bcf_dbf/bcf_dbf_rw 各 benchmark + diff_check。你只需把 lib.hgemm_bcf_dbf 换成 lib.abl_A1/abl_A2/abl_A2L/abl_A3(并且 A2/A2L 要额外准备 b_t = b.t().contiguous(),在计时之外)
157-160 if __name__ == "__main__": test_4096() 入口

2. kernels/hgemm/test_for_ncu.py(ncu 版)

3. kernels/sgemm/test.py + kernels/sgemm/readme.md(表格与命名范式)


三、"实验里调用后续要修改的 kernel 的代码原理"

这条链要读的是 kernels/hgemm/hgemm.cu 的 kernel 主体(它是你的实验基座,A3 = 现有 bcf_dbf 的读路径):

位置 内容 原理要点
hgemm.cu:22-40 CP_ASYNC_CG / CP_ASYNC_COMMIT_GROUP / CP_ASYNC_WAIT_GROUP_0 / LDMATRIX_X4 / LDMATRIX_X2 / LDMATRIX_X2_TRANS 全部是 asm volatile 内联 PTX。注意:SM80 多级流水需要 cp.async.wait_group N (N>0),本文件只有 WAIT_GROUP_0(宏名里的 0 就是 group 数) → 你要 STAGES=3 必须自己写 wait_group<1>,或从 kernels/hgemm_sm120/hgemm_sm120.cu 抄那段 cp_async_wait_group<N> 模板(那个文件整体 TMA 不可用,只抄这一个函数)
hgemm.cu:43-56 M16N8K16_F16 / M16N8K16_BF16 mma 指令封装,A/B fragment 寄存器布局
hgemm.cu:15-19 SWIZZLE_A/B/C 三个 swizzle 宏;A2/A2L 的预转置 B 需要新的 SWIZZLE_BT
hgemm.cu:58-201 hgemm_naive_kernel 最干净的读路径模板:grid swizzle(linear_id→bx,by)、cp.async 装载、ldmatrix 取 fragment、16 次 mma、写回 C。看这一份就懂"fragment 怎么来、mma 怎么吃"
hgemm.cu:203-353 hgemm_bcf_kernel + BCF swizzle
hgemm.cu:354-602 hgemm_bcf_dbf_kernel + 双缓冲(写死 2 buffer,你要改成 STAGES 模板参数就从这里改)
hgemm.cu:603-882 hgemm_bcf_dbf_rw_kernel + coalesced 写回(smem 暂存 C)
hgemm.cu:119-147(naive 内) LDMATRIX_X4 / LDMATRIX_X2_TRANS 的实际寻址 a_row/a_col/b_row/b_col 这几行就是 A3 的实现,你 A1/A2/A2L 要写的是同位置换寻址,其余(mma body、写回)照抄不动
hgemm.cu:884-917 CHECK_T + binding_tiled_func_gen host 侧 launcher(见 §1.2)

调用链全景(文档 §6 的代码依据):

test.py: lib = load(sources=[...])          # ninja 现场编译 → ablation_lib.so
      ↓ import 即执行 PYBIND11_MODULE → m.def("abl_A1", ...)  (pack.cuh:15)
      ↓ lib.abl_A1(a,b,c)
   C++ launcher (hgemm.cu:886 宏)  → 检查 contiguous → 取 M/N/K → grid/block → stream
      ↓ kernel<<<grid,256,smem,stream>>>(...)
   device: grid swizzle → cp.async gmem→smem → wait_group + __syncthreads
           → ldmatrix/ld.shared smem→reg → mma → 写回 C
      ↓
   benchmark(): warmup10 → sync → perf_counter → rep200 → sync → TFLOPS

四、"新增一组需要测试的 kernel" 具体要写哪些、写在哪

建议新建目录 kernels/ablation/,完全不动 kernels/hgemm/*(保持历史数据可复现),需要写 6 个文件:

1) kernels/ablation/ablation.cu —— 唯一的 C++ 主体

2) kernels/ablation/test.py —— 顶层实验接口(PyBIND 的消费端)

3) kernels/ablation/test_for_ncu.py + 4) kernels/ablation/run_ncu.sh

5) kernels/ablation/micro.cu + micro_test.py(文档 §4.3 强烈建议)

6) kernels/ablation/readme.md


五、动手前必须记住的 5 个"坑"(都来自上述文件)

  1. 模块名要唯一:name="ablation_lib" 与 hgemm_lib 不同,否则 torch 扩展缓存会串(缓存在 ~/.cache/torch_extensions)。
  2. sources 必须列全:.cu 增删后要改 sources(hgemm 目录 vs sgemm 目录的 name 分别是 hgemm_lib / gemm_lib,同一 .so 内多文件靠这个 list 合并)。
  3. extern __shared__ + cudaFuncSetAttribute 必须成对,否则 >48KB 直接 launch 失败。
  4. B 的形状契约随 B_LAYOUT 变:A1/A3 是 [K,N],A2/A2L 是 [N,K],launcher 与 Python 侧要同时改,且 CHECK_T 的 contiguous 要求不能破。
  5. TORCH_CUDA_ARCH_LIST 必须 export,且测性能前 nvidia-smi -lgc/-lmc 锁频、测完 -rgc/-rmc 解锁,否则噪声大于 A1↔A2 的 1~5% 效应量。

需要的话,下一步我可以按上面这份清单,把 kernels/ablation/ 的 6 个文件骨架写出来(仍然不改动 kernels/hgemm/*)。