mcpField
C
cuda-auto-tune

cuda-auto-tune

github.com/bruce-lee-ly开发25108 次安装29 周前更新
代码仓库

关于 cuda-auto-tune

面向 CUDA 内核开发者的优化技能,依据 NVIDIA Nsight Compute(NCU)分析数据指导迭代调优。它要求每次只做一项有数据依据的更改,覆盖 Native CUDA、CUTLASS、Triton 和 CuTe DSL。

主要功能

  1. 01NCU 性能分析与报告解读
  2. 02屋顶线、内存、停顿和占用率诊断
  3. 03按内核类型提供优化方案
  4. 04每次更改后重新分析并比较

适用场景

  1. 01诊断 CUDA 内核瓶颈
  2. 02调优 CUTLASS 或 Triton 内核
  3. 03比较代码更改前后的性能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bruce-lee-ly/cuda_auto_tune --skill cuda-auto-tune

安装后在 Claude Code 中输入/cuda-auto-tune确认已加载。

附带文件scripts/ncu_analyse.pyscripts/ncu_profile.sh

详细介绍

NCU 驱动的迭代式 kernel 优化(CUDA / CUTLASS / Triton / CuTe DSL)

门禁检查(在任何优化之前强制执行)

STOP — Do you have NCU profile data for this kernel?
  NO  → Go to Step 1. Do NOT touch any kernel code.
  YES → Go to Step 2.

硬性规则 — 违反任意一条都会使整个优化失效:

  • 没有 NCU 数据时,绝不可更改 kernel 代码、启动配置或模板参数。
  • 所有建议都必须引用具体的 NCU 指标值作为依据。
  • 每次迭代至少必须覆盖:roofline、内存层级、warp 停顿、占用率。
  • 优化手册必须与 kernel 的实现类型相匹配。
  • 每次代码更改后,都要重新 profile 并使用 --diff 进行比较。
  • 当改进趋于平缓或指标接近硬件上限时,停止迭代。

强制优化循环

┌─────────────────────────────────────────────────────────────────────┐
│  Step 1: Profile (NCU --set full)                                   │
│      ↓                                                              │
│  Step 2: Multi-dimensional analysis + identify kernel type          │
│      ↓                                                              │
│  Step 3: Apply type-specific playbook (one change per iteration)    │
│      ↓                                                              │
│  Step 4: Re-profile + diff → improved? → loop or stop               │
│      ↑                                           │                  │
│      └───────────────────────────────────────────┘                  │
└─────────────────────────────────────────────────────────────────────┘

步骤 1:使用 NCU 进行 profile(必需 — 没有数据就没有优化)

方案 A:Profiling 脚本(推荐)
# Native CUDA / CUTLASS binaries
bash cuda-auto-tune/scripts/ncu_profile.sh ./kernel report_v1

# Triton / Python
bash cuda-auto-tune/scripts/ncu_profile.sh "python your_kernel.py" report_v1

# CuTe DSL / Python
bash cuda-auto-tune/scripts/ncu_profile.sh "python your_cutedsl_kernel.py" report_v1

该脚本会收集 --set full → 导出 CSV → 运行深度分析 → 生成报告。

方案 B:手动 profiling
ncu --set full -o report_v1 --target-processes all ./your_kernel
ncu --import report_v1.ncu-rep --page raw --csv > report_v1.csv
python3 cuda-auto-tune/scripts/ncu_analyse.py report_v1.csv
Kernel 名称过滤器(减少噪声)
# CUTLASS only
ncu --set full -o report_v1 --target-processes all \
    --kernel-name "cutlass_\|sm90_\|ampere_" ./cutlass_program

# Triton only
ncu --set full -o report_v1 --target-processes all \
    --kernel-name "triton_" "python triton_kernel.py"

# CuTe DSL (kernel name often generic — use --type override in analysis)
python3 cuda-auto-tune/scripts/ncu_analyse.py report_v1.csv --type cutedsl
预期输出
ncu_reports/
├── report_v1.ncu-rep           # Full binary report
├── report_v1.csv               # Raw metrics CSV
├── report_v1_analysis.md       # Deep analysis report
└── report_v1_summary.txt       # Per-kernel summary

步骤 2:多维度分析

2.1 识别实现类型

根据 NCU 的 "Function Name" 和源代码上下文确定 kernel 类型:

类型检测信号
原生 CUDA没有库前缀;手写的 __global__ 函数
CUTLASScutlass_ 前缀、smXX_xmma_、包含 tensorop 或 cutlass
Tritontriton_ 前缀、包含 triton、带编码的后缀(例如 _0d1d...e)
CuTe DSL来自 @cute.kernel 的通用名称;通过源代码导入(cutlass.cute、cute.compile)或 --type cutedsl 确认
库cublas*、cudnn* — 仅作基线/参考,不可优化
2.2 通用诊断(所有 kernel 类型 — 始终运行)
维度关键 NCU 指标输出
RooflineSM 吞吐量、内存吞吐量compute-bound / memory-bound / latency-bound / balanced
内存层级L1/L2 命中率、合并访问比率、DRAM 吞吐量缓存效率 + 带宽子瓶颈(DRAM/L2/L1)
Warp 停顿PC 采样停顿原因(long_scoreboard、wait、barrier……)带百分比的 top 停顿原因
指令构成pipe FMA/ALU/LSU/Tensor 利用率流水线不均衡、Tensor Core 使用情况
占用率active warps %、限制因素分解(register/smem/warp/block)限制因素 + 寄存器数量 + smem 大小
内存隐患bank conflicts、寄存器溢出(local store sectors)严重程度与根本原因
发散平均执行线程数 vs 平均活跃线程数(true)发散百分比
2.3 类型特定的关注点
类型关键关注领域
原生 CUDA启动配置(block 大小、grid)、内存访问模式、异步拷贝(cp.async/TMA)、Tensor Core 机会
CUTLASSThreadblockShape、WarpShape、stages、对齐、调度策略、epilogue 融合、CTA swizzle
Tritonnum_warps、num_stages、BLOCK_* 大小、编译器提示(tl.multiple_of、tl.max_contiguous)、tl.dot 配置
CuTe DSLthreads_per_cta、elems_per_thread、CopyAtom(num_bits_per_copy)、tiled_copy 布局、smem 暂存、cta_reduce 模式
2.4 瓶颈分类决策树
SM% > MEM% + 20  →  COMPUTE_BOUND
MEM% > SM% + 20  →  MEMORY_BOUND
  ├─ DRAM throughput > 70%        → DRAM-Bound (near HBM ceiling)
  ├─ L2 hit < 50%, DRAM > 40%    → DRAM-Bound (L2 miss driven)
  ├─ L1 hit < 20%, L2 hit >= 50% → L2-Bound
  └─ L1 hit < 20%                → L1-Bound
SM% < 40 AND MEM% < 40           →  LATENCY_BOUND
SM% > 60 AND MEM% > 60           →  BALANCED (near peak)
2.5 结论模板(每次分析后必需)
=== Conclusion ===
Kernel:    {kernel_name}
Type:      {Native CUDA | CUTLASS | Triton | CuTe DSL}
Arch:      SM_{arch}
Overall:   {COMPUTE_BOUND | MEMORY_BOUND | LATENCY_BOUND | BALANCED}
Duration:  {duration_us} us
Roofline:  SM {sm}%, MEM {mem}%, DRAM {dram}%
Occupancy: {occ}% (theoretical: {theo}%), limited by {limiter}
Regs/Thread: {regs}, Smem/Block: {smem} KB

Findings (sorted by severity):
  [CRITICAL] {finding}: {NCU evidence with numbers} -> {specific action}
  [WARNING]  {finding}: {NCU evidence with numbers} -> {specific action}
  [INFO]     {finding}: {NCU evidence with numbers}

Optimization priorities:
  1. {highest_priority} (expected gain: Nx, evidence: {metric}={value})
  2. {second_priority}  (expected gain: Nx, evidence: {metric}={value})
  3. {third_priority}   (expected gain: Nx, evidence: {metric}={value})

步骤 3:应用类型特定的手册

不要仅凭直觉修改。每项更改都必须直接针对一条 NCU 发现。 每次迭代只应用一项更改,然后重新 profile(步骤 4)。


3.1 手册:原生 CUDA
3.1.1 启动配置
NCU 发现操作代码模式
占用率 < 50%,线程块大小 < 128将线程块大小增至 128–256kernel<<<grid, 256>>>
寄存器是占用率限制因素通过 __launch_bounds__ 限制寄存器__global__ void __launch_bounds__(256, 2) kernel()
网格过小(< SM 数量)确保有足够的线程块覆盖全部 SMgrid = (N + block - 1) / block,且 N 足够大
占用率低,线程块是限制因素减小线程块大小,使每个 SM 容纳更多线程块尝试用 128 替代 256
3.1.2 内存访问优化
NCU 发现操作代码模式
加载合并比率 > 8确保 warp 连续寻址,AoS→SoAdata[threadIdx.x + blockIdx.x * blockDim.x]
存储合并比率 > 8对分散写入使用共享内存中转先写入 smem,再合并写回
L1 命中率 < 20%对频繁复用的数据使用 __shared__通过 __syncthreads() 分块载入共享内存
L2 命中率 < 50%使用 L2 持久化提示(Ampere+)对热点数据范围使用 cudaAccessPolicyWindow
DRAM 吞吐 > 80%减少数据搬运:混合精度、压缩对带宽敏感的操作使用 half / __nv_bfloat16
Bank 冲突 > 100K对共享内存填充或重排布局__shared__ float smem[32][33];(填充 +1)
寄存器溢出 > 0减少每线程状态,使用 __launch_bounds__简化累加器,拆分为子 kernel
3.1.3 延迟隐藏与流水线
NCU 发现操作代码模式
stall_long_scoreboard > 30%(SM>=80)使用 cp.async + 双缓冲__pipeline_memcpy_async(&smem, &gmem, size)
stall_long_scoreboard > 30%(SM>=90)使用 TMA 进行批量异步传输cute::copy(tma_load, ...) 或 CuTe TMA atoms
stall_barrier > 25%降低同步频率,使用 warp 原语__shfl_sync()、cooperative_groups
stall_wait > 30%,long_scoreboard < 15%流水线缓冲过多,减少深度移除一级缓冲
stall_math_pipe_throttle > 20%计算已饱和(正面信号)考虑使用 Tensor Core 或减少 FLOPs
3.1.4 Tensor Core 利用率
NCU 发现操作
pipe_tensor < 5%,FP16/BF16 工作负载且呈 GEMM 类模式使用 WMMA(wmma::mma_sync)或内联 PTX(mma.sync)
pipe_tensor < 5%,但数据为 FP32通过 wmma::mma_sync 配合 nvcuda::wmma::precision::tf32 使用 TF32 路径
pipe_fma_fp16 > 10%,pipe_tensor < 5%从标量 FP16 FMA 切换到 Tensor Core 路径
3.1.5 向量化内存访问
// NCU evidence: coalescing ratio > 4 for 32-bit loads
// Before: scalar loads
float val = input[idx];

// After: vectorized 128-bit load (4x float)
float4 val = reinterpret_cast<const float4*>(input)[idx / 4];

3.2 实战手册:CUTLASS
3.2.1 Kernel 配置解析

CUTLASS kernel 名称编码了配置信息。提取以下内容:

  • 架构:sm80_、sm90_、ampere_、hopper_
  • 计算类型:tensorop 与 simt
  • Tile 形状:128x128x32、256x128x64
  • 流水线级数:末尾的 x3、x5
  • 对齐:align8
  • 调度(3.x):WarpSpecialized、WarpSpecializedCooperative、WarpSpecializedPingpong
3.2.2 Tile 形状与占用率
NCU 发现操作
占用率 < 40%,smem 是限制因素减小 ThreadblockShape(如 256x128→128x128)或减少级数
占用率 < 40%,寄存器是限制因素使用更小的 WarpShape(如 64x64→32x32)以减少每线程寄存器数
SM 吞吐 < 30%,网格较小增大 ThreadblockShape,使每个 CTA 处理更多元素
SM 吞吐 > 80%,MEM < 40%已是计算受限;增加流水线级数以获得更多重叠
3.2.3 流水线级数
NCU 发现操作
stall_long_scoreboard > 30%增加级数(Ampere:3→5,Hopper:2→3)
stall_wait > 30%,long_scoreboard < 15%流水线缓冲过多;减少级数以节省 smem
Smem 受限 + 级数 > 3减少级数以释放 smem,提高占用率
3.2.4 对齐与向量化
NCU 发现操作
Load coalescing > 4,alignment < 8将 CUTLASS alignment 提升至 8(128 字节);将矩阵 leading dims 填充至 alignment 的倍数
使用了 SIMT 路径但数据支持 TensorOp切换到 tensorop CUTLASS 配置(2–8 倍加速)
已配置 TensorOp 但 pipe_tensor < 5%检查对齐要求 —— LD 必须是 InstructionShape::kK 的倍数
3.2.5 调度与架构
NCU 发现操作
SM>=90 上使用 CUTLASS 2.x升级到 CUTLASS 3.x,使用 WarpSpecialized + TMA(1.2–1.5 倍收益)
大型 GEMM 上 L2 命中率 < 50%添加 ThreadblockSwizzle(2.x:GemmIdentityThreadblockSwizzle<N>,3.x:StreamK 或 tile swizzle)
Hopper 上 stall_long_scoreboard > 30%切换到使用 TMA 加载的 WarpSpecializedCooperative schedule
3.2.6 Epilogue 融合
NCU 发现操作
多个 CUTLASS kernel 连续执行(例如 GEMM + bias + activation)通过 CUTLASS epilogue visitor tree 融合为单个 kernel
DRAM 流量高(读+写 GB 超出预期)将 GEMM 后的操作移入 epilogue,以消除中间张量

3.3 Playbook:Triton
3.3.1 Kernel 分类

Triton kernel 子类型(来自 kernel 名称):

  • triton_poi_:Inductor pointwise(自动生成)
  • triton_red_:Inductor reduction(自动生成)
  • triton_per_:Inductor persistent reduction(自动生成)
  • 自定义 @triton.jit:手写 kernel(完全可调优)

Inductor 生成的 kernel:在 PyTorch 层面优化(torch._inductor.config),或者如果是热点路径,则重写为自定义 @triton.jit。

3.3.2 num_warps 调优
NCU 发现操作
Registers >= 128,num_warps >= 8CRITICAL:减少 num_warps(尝试 4 或 2)
Registers >= 64,num_warps >= 8将 num_warps 减少到 4
Occupancy < 40%,受寄存器限制减少 num_warps 和/或减少 BLOCK_* tile 尺寸
SM 吞吐 < 30%,warp 数量少增加 num_warps 以改善延迟隐藏
3.3.3 num_stages 调优
NCU 发现操作
stall_long_scoreboard > 30%增加 num_stages(Ampere 上 2→3→4,Hopper 上 2→3)
stall_wait > 30%,long_scoreboard < 15%减少 num_stages(缓冲区过量)或增加 tile 工作量
Smem 是 occupancy 限制因素减少 num_stages(每个 stage 会使 smem 缓冲区翻倍)
在 Hopper 上且 long_scoreboard 高同时考虑使用 tl.make_block_ptr() 进行基于 TMA 的加载
3.3.4 BLOCK_* tile 尺寸调优
NCU 发现操作
寄存器压力高减少 BLOCK_M、BLOCK_N 或 BLOCK_K
SM 吞吐低,存在计算受限机会增加 BLOCK_M/BLOCK_N,以增加每个 tile 的计算量
DRAM 带宽接近上限增加 BLOCK_K,以在写回前复用更多数据
3.3.5 内存访问优化
NCU 发现操作代码模式
Load coalescing > 8添加 stride 提示tl.multiple_of(stride, 16) 和 tl.max_contiguous(offsets, BLOCK)
转置输入上未合并访问使用结构化指针tl.make_block_ptr(base, shape, strides, offsets, block_shape, order)
L1 命中率低验证访问模式的连续性确保最内层维度 stride == 1
3.3.6 Tensor Core 利用率
NCU 发现操作
pipe_tensor < 5%,kernel 使用 tl.dot1)对 fp32 使用 allow_tf32=True;2)BLOCK_K 为 16 的倍数;3)检查 dtype 为 fp16/bf16/tf32/fp8
pipe_tensor < 5%,代码中没有 tl.dot缺少类 GEMM 模式 —— 重构以使用 tl.dot
3.3.7 Triton autotune 集成
@triton.autotune(
    configs=[
        triton.Config({'BLOCK_M': 128, 'BLOCK_N': 128, 'BLOCK_K': 32}, num_warps=4, num_stages=3),
        triton.Config({'BLOCK_M': 64,  'BLOCK_N': 64,  'BLOCK_K': 64}, num_warps=4, num_stages=4),
        triton.Config({'BLOCK_M': 128, 'BLOCK_N': 64,  'BLOCK_K': 32}, num_warps=8, num_stages=3),
    ],
    key=['M', 'N', 'K'],
)
@triton.jit
def kernel(...):
    ...

当 NCU 揭示瓶颈时,应将 autotune 配置缩小到有希望的区域,而不是盲目搜索。


3.4 Playbook:CuTe DSL
3.4.1 关键调优参数
参数作用典型范围
threads_per_cta每个 CTA 的 warp 数;影响占用率、屏障开销、reduce 开销128–512
elems_per_thread每个线程处理的元素数;影响寄存器压力、数据复用4–32
num_bits_per_copyCopyAtom 宽度;影响向量化加载/存储带宽32, 64, 128
Smem 暂存缓冲区流水线深度 × tile 大小;影响 smem 占用为提升占用率应尽量最小化
3.4.2 占用率优化
NCU 发现操作
占用率 < 40%,寄存器是限制因素减少 elems_per_thread 或减少 threads_per_cta;在 cute.compile() 中添加 --maxrregcount=128
占用率 < 40%,smem 是限制因素减少 threads_per_cta(更少的 warp → 更小的 reduce 缓冲区)或减少暂存缓冲区数量
寄存器 >= 128,warp >= 8CRITICAL:将 threads_per_cta 减少到 128 或 256
3.4.3 内存访问(TiledCopy)
NCU 发现操作
加载合并度 > 81) 将 num_bits_per_copy 提高到 128;2) 验证 t_layout 沿连续地址分布线程;3) 确保 from_dlpack() 使用 assumed_align=16
stall_long_scoreboard > 30%1) 将 num_bits_per_copy 提高到 128;2) 增加 elems_per_thread 以实现更多复用;3) 在 SM>=80 上使用 CpAsyncOp copy atom;4) 添加双缓冲
stall_wait > 30%,long_scoreboard < 15%流水线缓冲区过多;增加 elems_per_thread 以在每个阶段进行更多计算,或减少流水线深度
3.4.4 同步与归约
NCU 发现操作
stall_barrier > 25%1) 减少 threads_per_cta(屏障处更少的 warp);2) 将第二个 sync_threads 替换为 shuffle 广播(如果 warp <= 32);3) 合并多个 cta_reduce 调用
高屏障 + 小归约对于少量元素,使用仅 warp 归约而不使用 smem
每次迭代多次 sync_threads最小化同步点;使用异步流水线 commit/wait 模式
3.4.5 线程分歧
NCU 发现操作
分歧 > 20%调整 threads_per_cta * elems_per_thread 以紧密匹配问题维度 N,减少被谓词关闭的线程
谓词化拷贝显示高分歧确保 N 能被 threads_per_cta * elems_per_thread 整除,或使用尾部处理策略
3.4.6 计算优化
NCU 发现操作
pipe_tensor < 5%,类 FP16 GEMM 操作使用 cute.make_mma_atom() 搭配 MmaOp 走 Tensor Core 路径
pipe_fma 高但 pipe_tensor 低(如 RMSNorm/LayerNorm 等非 GEMM 操作)Tensor Core 不适用于归约 — 应转而专注于内存和屏障优化
3.4.7 重新分析前的缓存失效

CuTe DSL 通过 JIT 将 Python 编译为 CUDA。代码更改后:

# Clear compilation cache to ensure re-compilation
rm -rf __pycache__/ .cache/ /tmp/cutlass_cute_cache/
# Then re-profile
bash cuda-auto-tune/scripts/ncu_profile.sh "python your_cutedsl_kernel.py" report_v2

步骤 4:重新分析和验证(每次更改后必需)

4.1 重新分析
# Clear JIT caches first
rm -rf ~/.triton/cache            # Triton
rm -rf __pycache__/ .cache/       # CuTe DSL

# Profile updated version
bash cuda-auto-tune/scripts/ncu_profile.sh ./kernel_v2 report_v2
# or
bash cuda-auto-tune/scripts/ncu_profile.sh "python kernel_v2.py" report_v2
4.2 与基线对比
python3 cuda-auto-tune/scripts/ncu_analyse.py ncu_reports/report_v2.csv --diff ncu_reports/report_v1.csv
4.3 验证清单
检查项标准
耗时有改善?gpu__time_duration.sum 降低
目标瓶颈有改善?触发更改的特定指标有改善
无新瓶颈?差异报告中无新的 CRITICAL 发现
达到硬件上限?SM 吞吐量 > 80% 或 DRAM 吞吐量 > 85% 表示接近峰值
4.4 迭代日志模板

跟踪每次迭代以便问责:

=== Iteration {N} ===
Change:  {what was changed and why}
NCU evidence: {metric}={before_value} -> {finding}
Report: report_v{N}.csv

Result:
  Duration: {before} us -> {after} us ({delta}%)
  Target metric: {metric}={before} -> {after}
  New findings: {any new issues introduced}

Decision: {CONTINUE to next bottleneck | STOP — at ceiling | ROLLBACK — regression}

快速参考:高信号 NCU 指标

指标NCU 键
时长gpu__time_duration.sum [us]
SM 吞吐量sm__throughput.avg.pct_of_peak_sustained_elapsed [%]
内存吞吐量gpu__compute_memory_throughput.avg.pct_of_peak_sustained_elapsed [%]
DRAM 吞吐量gpu__dram_throughput.avg.pct_of_peak_sustained_elapsed [%]
L1 命中率l1tex__t_sector_hit_rate.pct [%]
L2 命中率lts__t_sector_hit_rate.pct [%]
加载合并l1tex__t_sectors_pipe_lsu_mem_global_op_ld.sum / l1tex__t_requests_pipe_lsu_mem_global_op_ld.sum
Bank 冲突l1tex__data_bank_conflicts_pipe_lsu_mem_shared.sum
寄存器溢出l1tex__t_sectors_pipe_lsu_mem_local_op_st.sum [sector]
占用率sm__warps_active.avg.pct_of_peak_sustained_active [%]
Warp 可调度性smsp__warps_eligible.avg.per_cycle_active [warp]
寄存器/线程launch__registers_per_thread [register/thread]
共享内存/块launch__shared_mem_per_block [Kbyte/block]

摘要

本技能强制执行严格的 profile → analyze → change → verify 循环。 没有 NCU 数据 = 不进行优化。没有指标证据 = 不修改代码。 每种 kernel 类型(Native CUDA / CUTLASS / Triton / CuTe DSL)都有专门的 playbook, 其中包含 NCU 指标到操作的映射。每次更改都会被跟踪,并通过重新 profile 进行验证。

评论

支持 Markdown