返回技能列表

pipeline-gpu-kernel

pjt222
更新于 Yesterday
5 次查看
17
2
17
在 GitHub 上查看
其他general

关于

This skill applies software pipelining (double-buffering) to tiled GPU kernels to overlap global memory loads with Tensor Core computations. It restructures kernels into prologue/main-loop/epilogue sections and provides variants using either LDG registers or cp.async (LDGSTS) based on compute-to-load ratio. It's used for memory-bound kernels with low compute-to-load ratios where warp interleaving alone cannot hide DRAM latency.

快速安装

Claude Code

推荐
主要方式
npx skills add pjt222/agent-almanac -a claude-code
插件命令备选方式
/plugin add https://github.com/pjt222/agent-almanac
Git 克隆备选方式
git clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/pipeline-gpu-kernel

在 Claude Code 中复制并粘贴此命令以安装该技能

技能文档

流水 GPU 核

施軟體流水(雙緩衝)於分塊 GPU 核,使第 N+1 塊之全域記憶體載入與第 N 塊之 Tensor Core 算覆於一時。化序之載-同步-算-同步 K 環為序章/環/尾章之構,依算載比擇 LDG 寄存器或 cp.async (LDGSTS) 之變體,驗共享記憶體不逾架構占用懸崖,終於 SASS 確載算之覆。

用時

  • analyze-kernel-bottleneck 識為記憶體受限之核、每塊算載比低乃用
  • 唯倚 warp 交錯不足以掩 DRAM 延遲(GA104 約 300 週期)乃用
  • 核含序之載-同步-算-同步 K 環、可重構乃用
  • 算載比高(>20:1)且 8+ warp 活者,不需

  • 必要:CUDA 核源文件(.cu),含分塊 K 環,載與算分立
  • 必要:目標 GPU 架構(如 GA104 / sm_86 — 定 smem 懸崖與占用上限)
  • 必要:當前塊大小(BM, BN, BK)及數據型(FP16, FP32, INT8)
  • 可選:每塊算載比(自 analyze-kernel-bottleneck;闕則估之)
  • 可選:基準(目標問題大小下未流水之效)

第一步:驗前提

確認核含分塊 K 環、載與算二相隔以 __syncthreads()。算雙倍共享記憶體之費,驗其不逾架構占用懸崖。

  1. 於核中尋 K 環。其當有此序之構:自全域載 A B 二塊入共享記憶體、__syncthreads()、於共享記憶體塊上算(HMMA/IMMA/FFMA)、__syncthreads()
  2. 記單緩衝共享記憶體之大小:smem_a_size = BM * BK * sizeof(T)smem_b_size = BK * BN * sizeof(T)
  3. 算雙緩衝之費:smem_doubled = smem_a_size * 2 + smem_b_size * 2
  4. 對架構懸崖比之。GA104 (sm_86):每 SM 最大 100 KB smem,懸崖在每塊 50 KB(逾 50 KB = 每 SM 1 塊 = 4 warp,占用減半)。
單緩衝:smem_a[BM*BK] + smem_b[BK*BN] = 2 KB + 2 KB = 4 KB
雙緩衝:smem_a[2][BM*BK] + smem_b[2][BK*BN] = 4 KB + 4 KB = 8 KB
8 KB << 50 KB 懸崖 -> 每 SM 2 塊 -> 8 warp
  1. 驗環迭次:num_tiles = K / BK。流水需 num_tiles >= 2(至少一序章 + 一主環迭)。

得:共享記憶體預算之表,列單緩衝與雙緩衝之費,確認雙倍配置不逾架構懸崖、每 SM 至少 2 塊占用。

敗則:若雙緩衝逾懸崖,減塊大小(BK 或 BM 減半)至 smem_doubled <= 50 KB(GA104)。或用唯寄存器之預取(LDG 變體)不雙倍共享記憶體——預取數據存於寄存器,於 __syncthreads() 後寫入同一單緩衝。

第二步:擇變體

依每塊算載比於 LDG 寄存器與 cp.async (LDGSTS) 間擇之。

  1. 算算載比:GEMM 類核之 ratio = (2 * BM * BN * BK) / ((BM * BK + BK * BN) * sizeof(T))(每乘加 2 FLOP,每塊載入字節)。
  2. 用此規則:

LDG 寄存器變體(ratio >= 5 或 CUDA < 11.0):

  • LDG 第 N+1 塊入寄存器(非阻塞之全域載入)。
  • buf[N % 2] 算(與飛行中之 LDG 覆)。
  • __syncthreads(),後 STS 寄存器入 buf[(N+1) % 2]__syncthreads()
  • 實作較簡,無流水 API 之依。
  • 增寄存器壓力:每線程約 (BM * BK + BK * BN) / BLOCK_SIZE 寄存器供暫存。

cp.async (LDGSTS) 變體(ratio < 5,CUDA >= 11.0):

  • __pipeline_memcpy_async 第 N+1 塊直入 buf[(N+1) % 2](異步,繞寄存器檔)。
  • 算前 __pipeline_commit()
  • buf[N % 2] 算。
  • 算後 __pipeline_wait_prior(0) + __syncthreads()
  • 覆更佳,預取無寄存器壓力。需 #include <cuda_pipeline.h>
  1. 決閾(測於 GA104,IGEMM 4096x4096x4096):
    • 比 < 5:1 — 取 cp.async(IGEMM 測得 +35%)。
    • 比 5-20:1 — 二者皆實作而基準擇之。
    • 比 > 20:1 — 流水未必有益(warp 交錯已足)。

得:所擇變體並依算載比與目標架構之說明。

敗則:若比模糊(5-20:1 之間),二變體皆實作而基準。CUDA 版本支持時,cp.async 為較穩之預設。

第三步:重構 K 環

化序之載-同步-算-同步環為流水之序章/環/尾章構。

  1. 識三段:原環體化為三段:

    • 序章:載第 0 塊入 buf[0],同步,後入主環。
    • 主環:第 1 塊至 num_tiles - 1 塊,使第 N+1 塊之載入與第 N 塊之算覆。
    • 尾章:算最後一塊(已於主環末迭載入)。
  2. LDG 寄存器變體之構

// === LDG-register variant ===
// Prologue: load tile 0 into buf[0]
cooperative_load_tile(smem_a[0], smem_b[0], global_a, global_b, /*k_offset=*/0);
__syncthreads();

for (int tile = 0; tile < num_tiles - 1; tile++) {
    int cur_buf = tile & 1;
    int next_buf = 1 - cur_buf;

    // Phase 1: LDG next tile into registers (non-blocking)
    float reg_a[ELEMS_PER_THREAD_A], reg_b[ELEMS_PER_THREAD_B];
    prefetch_tile_to_registers(reg_a, reg_b, global_a, global_b,
                               (tile + 1) * BK);

    // Phase 2: Compute on current buffer (overlaps with LDG flight)
    tensor_core_mma(smem_a[cur_buf], smem_b[cur_buf], acc);

    // Phase 3: Drain registers into next buffer
    __syncthreads();
    store_registers_to_smem(smem_a[next_buf], smem_b[next_buf],
                            reg_a, reg_b);
    __syncthreads();
}

// Epilogue: compute last tile
tensor_core_mma(smem_a[(num_tiles - 1) & 1], smem_b[(num_tiles - 1) & 1], acc);
  1. cp.async 變體之構
// === cp.async variant ===
#include <cuda_pipeline.h>

// Prologue: async load tile 0 into buf[0]
cpasync_load_tile(smem_a[0], smem_b[0], global_a, global_b, /*k_offset=*/0);
__pipeline_commit();
__pipeline_wait_prior(0);
__syncthreads();

for (int tile = 0; tile < num_tiles - 1; tile++) {
    int cur_buf = tile & 1;
    int next_buf = 1 - cur_buf;

    // Phase 1: cp.async next tile into next buffer (async, direct to smem)
    cpasync_load_tile(smem_a[next_buf], smem_b[next_buf],
                      global_a, global_b, (tile + 1) * BK);
    __pipeline_commit();

    // Phase 2: Compute on current buffer (overlaps with LDGSTS in flight)
    tensor_core_mma(smem_a[cur_buf], smem_b[cur_buf], acc);

    // Phase 3: Wait for async copies to complete
    __pipeline_wait_prior(0);
    __syncthreads();
}

// Epilogue: compute last tile
tensor_core_mma(smem_a[(num_tiles - 1) & 1], smem_b[(num_tiles - 1) & 1], acc);
  1. 驗環次:主環行 num_tiles - 1 迭(指算之第 0 至 num_tiles - 2 塊,載入第 1 至 num_tiles - 1 塊)。尾章算末迭所載之塊。

得:所擇變體之重構 K 環源碼,序章、主環、尾章三段分明。

敗則:最常見之誤為緩衝索引差一或忘尾章算。驗:序章載入 buf[0],主環首迭算 buf[0] 而載入 buf[1],次迭算 buf[1] 而載入 buf[0],餘類推。尾章算 buf[(num_tiles - 1) & 1]

第四步:實作雙緩衝

聲明雙緩衝共享記憶體並實作載入函數。

  1. 易單緩衝共享記憶體聲明為雙緩衝陣列:
// Before (single buffer)
__shared__ half smem_a[BM * BK];
__shared__ half smem_b[BK * BN];

// After (double buffer)
__shared__ half smem_a[2][BM * BK];
__shared__ half smem_b[2][BK * BN];
  1. cp.async 變體者,以流水 API 實作異步載入函數:
__device__ void cpasync_load_tile(half* dst_a, half* dst_b,
                                  const half* src_a, const half* src_b,
                                  int k_offset) {
    // Each thread copies its portion (16 bytes = 8 half values per cp.async)
    int tid = threadIdx.x;
    int bytes_per_thread = 16;  // cp.async.cg supports 4, 8, or 16 bytes

    // A tile: BM * BK elements, distributed across BLOCK_SIZE threads
    int elems_a = BM * BK / BLOCK_SIZE;
    for (int i = 0; i < elems_a; i += 8) {
        int idx = tid * elems_a + i;
        __pipeline_memcpy_async(dst_a + idx,
                                src_a + k_offset * BM + idx,
                                bytes_per_thread);
    }

    // B tile: BK * BN elements, distributed similarly
    int elems_b = BK * BN / BLOCK_SIZE;
    for (int i = 0; i < elems_b; i += 8) {
        int idx = tid * elems_b + i;
        __pipeline_memcpy_async(dst_b + idx,
                                src_b + k_offset * BN + idx,
                                bytes_per_thread);
    }
}
  1. LDG 變體者,實作寄存器暫存陣列與儲存函數:
// Declare register staging (size = elements per thread)
half reg_a[BM * BK / BLOCK_SIZE];
half reg_b[BK * BN / BLOCK_SIZE];

// Prefetch: LDG from global to registers (non-blocking, issued early)
for (int i = 0; i < BM * BK / BLOCK_SIZE; i++) {
    int idx = threadIdx.x * (BM * BK / BLOCK_SIZE) + i;
    reg_a[i] = global_a[k_offset * BM + idx];
}
// ... similarly for reg_b

// Store: STS from registers to shared memory (after __syncthreads)
for (int i = 0; i < BM * BK / BLOCK_SIZE; i++) {
    int idx = threadIdx.x * (BM * BK / BLOCK_SIZE) + i;
    smem_a[next_buf][idx] = reg_a[i];
}
  1. __launch_bounds__(BLOCK_SIZE) 於核,俾編譯器得占用之確息。
  2. 編譯:nvcc --cubin -arch=sm_86 -O2 -o kernel.sm_86.cubin kernel.cu

得:可編譯之核,含雙緩衝共享記憶體與所擇載入機制。cubin 順生而無誤。

敗則:若編譯敗於流水 API,確 #include <cuda_pipeline.h> 在、CUDA toolkit >= 11.0。若寄存器溢(察 nvcc --resource-usage),減寄存器暫存陣列之大小,或增 BLOCK_SIZE 或減 BK。

第五步:驗正確

行流水核對 CPU 參考,確數值輸出相同。

  1. 編基準:nvcc -arch=sm_86 -O2 -o bench bench.cu -lcuda -I../../phase2/common
  2. 先以小問題(512x512x512)行之,捕索引之誤而後擴。
  3. 依數據型用合宜之容差:
    • INT8 Tensor Core (IMMA):abs=0.5, rel=0.1
    • FP16 Tensor Core (HMMA):abs=1e-2, rel=1e-2
    • FP32 標量 (FFMA):abs=1e-3, rel=1e-3
  4. 流水不變算術——唯重序載入。若正確敗,誤在緩衝索引、非算邏輯。
  5. 於目標問題大小(如 4096x4096x4096)測之,驗邊界處理。

得:小與目標二大小皆 PASS,誤差界與未流水基準相同。

敗則:緩衝索引誤為最可能之因。驗:算讀自 buf[tile & 1],載寫至 buf[1 - (tile & 1)]。察尾章處理之緩衝索引為 (num_tiles - 1) & 1、非 num_tiles & 1。cp.async 者,驗 __pipeline_wait_prior(0)__syncthreads() 前畢——否則算或讀部分寫之數據。

第六步:基準與比

於目標問題大小,量流水核對未流水基準。

  1. 行未流水基準,記 GFLOPS 或頻寬(依核型)。
  2. 行各流水變體,記同指標。
  3. 算加速:speedup = pipelined_metric / baseline_metric
  4. 依算載比之預期所得(測於 GA104):
    • 低比(<5:1):cp.async +15-35%(IGEMM 測:LDG +18%,cp.async +35% 於 4096x4096x4096)。
    • 中比(5-20:1):+5-15%。
    • 高比(>20:1):0-5% 或退步。
  5. 若二變體皆實作,擇其速者用於生產。
| Variant          | GFLOPS | Speedup vs Baseline |
|------------------|--------|---------------------|
| Baseline         | XXX    | 1.00x               |
| LDG-register     | XXX    | X.XXx               |
| cp.async (LDGSTS)| XXX    | X.XXx               |

得:效能比較表示提升。所擇變體當顯可量加速,與算載比之預測相合。

敗則:若效退,察三事:(1) SASS 有未料之指令額外開銷(額外 BAR.SYNC、寄存器溢)。(2) 共享記憶體未越占用懸崖——以 nvcc --resource-usagecuobjdump -res-usage 驗。(3) 問題大小生足塊(K / BK >= 4)以攤序章/尾章之開銷。

第七步:驗 SASS 之覆

察編譯之 SASS,確全域載入與 Tensor Core 指令於主環體內覆。

  1. 反組譯:cuobjdump -sass kernel.sm_86.cubin | grep -E 'IMMA|HMMA|LDGSTS|LDG|BAR'
  2. 於主環體內,驗此序之模式:
    • LDGSTSLDG 指令現於 HMMAIMMA 指令之前
    • 載入指令與算指令間無 BAR.SYNC(必使其於 warp 排程器中得覆)。
    • BAR.SYNC 現於算塊之後,閘下迭對所載數據之用。
  3. 察 HMMA/IMMA 指令之停滯碼——HMMA 流水延遲之 S08 為預期不可避。IMMA 之 S01-S04 為常。LDG/LDGSTS 之停滯當低(S01),warp 排程器於載飛行中得切至算。
  4. 計每環迭之 HMMA/IMMA 指令總數——當與未流水版相符(流水不當改算量)。
# Full SASS pipeline verification
cuobjdump -sass kernel.sm_86.cubin | grep -E 'IMMA|HMMA|LDGSTS|LDG|BAR'

# Count compute instructions per loop
cuobjdump -sass kernel.sm_86.cubin | grep -c 'HMMA\|IMMA'

# Check for register spills
nvcc --resource-usage --cubin -arch=sm_86 -O2 kernel.cu 2>&1 | grep -i spill

得:注釋 SASS 摘錄示載先算後之模式,無中介屏障。零寄存器溢。

敗則:若編譯器將載入重序於算後(破覆),試:(1) 主環上 #pragma unroll 1 防過激展開。(2) 將載與算分至獨立行內函數以為序之提示。(3) 用 asm volatile("" ::: "memory") 為載算塊間之編譯器籬笆(末手段——或抑他優化)。

  • 雙緩衝 smem 不逾架構懸崖(GA104:每塊 50 KB)
  • 二緩衝交替而用(buf[tile & 1] 模式)
  • 序章載第 0 塊入 buf[0]
  • 尾章算末塊自 buf[(num_tiles - 1) & 1]
  • 對 CPU 參考於小與目標大小皆 PASS
  • SASS 確載算之覆(LDGSTS/LDG 與 IMMA/HMMA 間無 BAR.SYNC
  • 效優於未流水基準
  • LDG 變體無寄存器溢(察 nvcc --resource-usage

  • 倍緩衝越 smem 懸崖 — GA104 懸崖在每塊 50 KB、非 64 KB。實作前必算 smem_doubled。單緩衝用 28 KB 之核,雙倍後跳至 56 KB 越懸崖、占用減半。流水之 +20% 益可化為 -50% 之占用退。
  • 忘尾章算 — 主環末迭所載之末塊,環外需自身之算相。闕之,K 維末 BK 列默墮,致誤而似小數值差、非顯敗。
  • 緩衝索引差一 — 算用 buf[tile & 1],載用 buf[1 - (tile & 1)]。常誤為以 buf[(tile + 1) & 1] 為下一緩衝,緩衝數為 2 時等同 buf[1 - (tile & 1)]——然若誤施於算索引則讀錯。
  • cp.async 提交/等待之序__pipeline_commit() 必於算相呼之(封異步副本之批)。__pipeline_wait_prior(0) 必於算相呼之(阻至所提之副本皆畢)。互換之,異步副本變同步,盡失覆益。
  • 缺 __syncthreads — LDG 變體者,算與 STS 排空間需 __syncthreads()(俾算先讀畢當前緩衝再被覆)。STS 排空後另需 __syncthreads()(俾諸線程寫畢,下迭再讀)。cp.async 變體者,__pipeline_wait_prior(0) 後之 __syncthreads() 確諸線程見畢之異步副本。
  • cp.async 之邊界處理__pipeline_memcpy_async 需源址有效且對齊。矩陣邊處 K 不為 BK 之倍時,末塊或越界讀。對末塊回退用標量載入並界檢,或將輸入矩陣補至 BK 之倍。

  • analyze-kernel-bottleneck — 識核是否為記憶體受限,並算驅變體擇之算載比

GitHub 仓库

pjt222/agent-almanac
路径: i18n/wenyan/skills/pipeline-gpu-kernel
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

相关推荐技能

llamaguard

其他

LlamaGuard是Meta推出的7-8B参数内容审核模型,专门用于过滤LLM的输入和输出内容。它能检测六大安全风险类别(暴力/仇恨、性内容、武器、违禁品、自残、犯罪计划),准确率达94-95%。开发者可通过HuggingFace、vLLM或Sagemaker快速部署,并能与NeMo Guardrails集成实现自动化安全防护。

查看技能

cost-optimization

其他

这个Claude Skill帮助开发者优化云成本,通过资源调整、标记策略和预留实例来降低AWS、Azure和GCP的开支。它适用于减少云支出、分析基础设施成本或实施成本治理策略的场景。关键功能包括提供成本可视化、资源规模调整指导和定价模型优化建议。

查看技能

quantizing-models-bitsandbytes

其他

这个Skill使用bitsandbytes库量化大语言模型,能在GPU内存有限时通过8位或4位量化减少50-75%内存占用,同时保持精度损失最小。它支持INT8、NF4、FP4等多种量化格式,可与HuggingFace Transformers无缝集成,适用于需要部署更大模型或加速推理的场景。还提供QLoRA训练和8位优化器支持,让开发者能轻松实现高效模型压缩。

查看技能

dispatching-parallel-agents

其他

该Skill用于并行处理3个以上无依赖关系的独立故障,可为每个问题域分派专属Claude代理同时执行调查修复。它通过并发处理多个独立问题显著提升故障排查效率,特别适用于测试文件、子系统等无共享状态的场景。

查看技能