Skip to main content

inference-kernel-ncu-profile

Capture per-kernel CUDA hardware-counter data (occupancy, achieved warps active, regs/thread, smem/block, DRAM throughput, arithmetic intensity, branch divergence %, warp-stall reason %) from a live vLLM inference pod via an `ncu` debug sidecar (no production image rebuild). Outputs `.ncu-rep` + per-kernel CSV. Scoped via `--kernel-name` to avoid the 10-100x kernel-replay slowdown that an unscoped ncu attach would impose on serving. Pairs with `inference-kernel-profile` (nsys: absolute kernel duration + cuda-graph timeline) and `zymtrace-anchored-query` (sample- share + kernel-name resolution). Triggers on "ncu", "nsight compute", "kernel internals", "occupancy", "register pressure", "shared memory", "DRAM bandwidth", "roofline", "arithmetic intensity", "warp stalls", "branch divergence", or any combination of "ncu / nsight-compute / occupancy / regs / smem / dram / roofline" with "vllm / kimi / glm / deepseek / inference".

来源信息

仓库
cfregly/claude-gpu-perf-tune
最近来源活动
2026年6月14日 03:33
检测到的 SKILL.md 语言
英语
星标
0
分支
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
inference-kernel-ncu-profile
last_validated
2026-05-29T00:00:00.000Z
description
Capture per-kernel CUDA hardware-counter data (occupancy, achieved warps active, regs/thread, smem/block, DRAM throughput, arithmetic intensity, branch divergence %, warp-stall reason %) from a live vLLM inference pod via an `ncu` debug sidecar (no production image rebuild). Outputs `.ncu-rep` + per-kernel CSV. Scoped via `--kernel-name` to avoid the 10-100x kernel-replay slowdown that an unscoped ncu attach would impose on serving. Pairs with `inference-kernel-profile` (nsys: absolute kernel duration + cuda-graph timeline) and `zymtrace-anchored-query` (sample- share + kernel-name resolution). Triggers on "ncu", "nsight compute", "kernel internals", "occupancy", "register pressure", "shared memory", "DRAM bandwidth", "roofline", "arithmetic intensity", "warp stalls", "branch divergence", or any combination of "ncu / nsight-compute / occupancy / regs / smem / dram / roofline" with "vllm / kimi / glm / deepseek / inference".
allowed-tools
["Bash(kubectl:debug,*)","Bash(kubectl:exec,*)","Bash(kubectl:cp,*)","Bash(kubectl:get,*)","Bash(ncu:*)","Bash(jq:*)","Bash(sha256sum:*)","Read","Write"]
# inference-kernel-ncu-profile ## Purpose Capture per-kernel **hardware-counter** data from a **live vLLM inference pod** without rebuilding the production image. Uses `kubectl debug --share-processes` to attach an `ncu`-enabled sidecar to an existing pod. The sidecar replays a small number of launches of each named kernel with the hardware-counter set enabled and writes `.ncu-rep` files into a sidecar emptyDir that you extract via `kubectl cp`. The three sibling skills cover complementary kernel-profile dimensions: | Question | Tool | Skill | |---|---|---| | What kernels run + relative sample-share? | zymtrace | `zymtrace-anchored-query` | | Absolute per-kernel duration (ns), CUDA graph timeline, NVTX ranges | nsys | `inference-kernel-profile` | | **Per-kernel occupancy / regs / smem / DRAM-BW / arithmetic intensity / warp stalls** | **ncu** | **this skill** | | c=1 decode-step budget (GPU-busy vs host-gap) + kernel-vs-host-vs-comm verdict | vLLM profiler endpoint / nsys | `inference-decode-step-budget` | **Sequencing note:** for the decode/latency tier, run [`inference-decode-step-budget`](/plugins/profile-and-optimize/skills/inference-decode-step-budget/SKILL.md) FIRST. If at c=1 it returns "host-bound" (GPU idle >> busy per step, as GLM-5.1 does), per-kernel SoL is **moot** - the kernels are a small fraction of TPOT and are launch-latency-scale at one token. Only escalate to this skill when the budget says kernel-bound, or for the prefill/throughput tier. ## When to use - Investigating WHY a specific kernel is hot. The `zymtrace-anchored-query` + `inference-kernel-profile` skills tell you WHICH kernels are hot and HOW LONG they take. Ncu tells you whether they're memory-bound vs compute-bound, whether occupancy is the ceiling, whether warps are stalling on memory or sync, etc. - Roofline analysis to prove a kernel is at-the-roof vs has-room. - Pre-PR-submission justification: when proposing a custom kernel or a kernel-tuning change, ncu evidence (e.g. "this kernel achieves only 47% theoretical occupancy due to register pressure") is the kind of argument upstream reviewers expect. Do **not** use this skill for: - Continuous always-on capture - kernel replay is 10-100x slow per replayed kernel. Ncu is a per-shot tool. - Untargeted "give me everything" sweeps - without `--kernel-name` scoping, ncu will replay EVERY kernel, which on a busy vLLM pod is a heavy operation that risks Kubelet liveness-probe failures. - Multi-pod fleet capture - this skill targets ONE pod at a time. ## Sidecar image Same image as `inference-kernel-profile`: **`ghcr.io/cfregly/nsys-sidecar:0.1.0`** (publicly readable). Image includes nsys 2025.6.3 + **ncu 2026.1.1** + py-spy 0.4.1 + python3 + jq. Image is 11.87 GB. First-pull on a new GPU node is ~3-7 min from ghcr.io. Subsequent attaches on the same node are instant. ## Safety: live-serving impact ncu's `--set full` capture flag enables every hardware-counter group, which forces kernel replay (the kernel is launched, ncu reads the counters, the kernel is launched AGAIN, ncu reads the next group, ...). Replay multiplies the kernel's wall-clock by 10-100x. **On a single-replica deploy**, the replay slowdown can spike pod CPU to the point of Kubelet liveness-probe failure. Mitigate by: - Using `--launch-count 3` not 5 - Using `--set basic` instead of `--set full` for the first capture (basic captures only the metric groups required for roofline, ~3x slowdown instead of 30x) - Scoping to ONE kernel per capture, not 5 **On a multi-replica deploy**, a temporary per-replica capacity dip during ncu replay is usually acceptable, so `--launch-count 5` + `--set full` is safe - the slow replica is just 1 of N and the Service load-balancer distributes around it. **Fallback**: if even the conservative path is too risky, spin a sister deploy `<deploy>-ncu` at 0 service routing (a separate Helm release serving the same model, NOT in the Service selector), and ncu against THAT pod. Adds ~30 min for the sister helm install, but zero risk to production serving. **Memory caveat:** at `gpu-mem-util >= 0.85` there is little headroom for a profiler's host/device buffers. A long capture window (observed with a 40s nsys window on a GLM-5.1 canary) can OOM-crash vllm mid-capture and truncate the report. Keep windows short, scope ncu to one kernel + low `--launch-count`, and prefer the endpoint-triggered bounded windows in [`inference-decode-step-budget`](/plugins/profile-and-optimize/skills/inference-decode-step-budget/SKILL.md). ## Gate 0 (precondition): CUPTI must even initialize (CUDA image-vs-driver skew) ncu is a CUPTI client, so on GB300 nodes it can record **0 kernels** for the same reason nsys + the vLLM torch profiler do: a CUDA 12.9 serve image against the node's CUDA 13.0 driver makes CUPTI fail to init (`CUPTI_ERROR_INVALID_DEVICE`). This is NOT permission (`RmProfilingAdminOnly:0`), NOT a missing lib (`libcupti.so.12` present + linked), and NOT a wrong-process attach - it is a CUDA major-version toolkit-vs-driver skew. If ncu returns 0 kernels, grep the launch log for `CUDA versions. CUPTI/Runtime/Driver`: a 12.x-toolkit / 13.x-driver split needs a CUDA-13-aligned image or zymtrace (non-CUPTI), not more attach tuning. **SOLVED PATH (GB300 default): pull a Blackwell-capable ncu from NGC** instead of using the in-image ncu: copy ncu **2026.x** from `nvcr.io/nvidia/pytorch:26.05-py3` (arm64/sbsa) into the serve container via an initContainer (no prod rebuild. Needs an NGC image-pull secret in the namespace). Use this NGC-tools path on GB300 for both nsys and ncu, the legacy in-image / apt paths are superseded there. Verified end-to-end on a GB300 MoE deploy (per-kernel Compute(SM)/Occupancy resolved). ## ncu capture-hygiene (empty-rep / non-deterministic gates) - empty != tooling limit Beyond Gate 0 (CUPTI), three ncu traps on a vLLM stack each yield "No kernels were profiled" or a non-deterministic-count failure. NONE is a tooling limit (each reproduced + fixed on a DeepSeek-V4-Flash GB300 deploy). Canon: `docs/METHODOLOGY.md` "Capture hygiene". 1. **`cudaProfilerStart` breaks under any vLLM `direct_register_custom_op` import.** If your harness imports a module that runs `direct_register_custom_op` at load (`mhc.tilelang`, `fp8_utils`, …), ncu `--profile-from-start off` + an in-script `torch.cuda.cudart().cudaProfilerStart()` captures **0 kernels** (the custom-op/triton registration perturbs the CUPTI profiler-start hook). FIX: do NOT rely on cudaProfilerStart - profile from start and exclude warmup with `--launch-skip <warmup> --launch-count N` + `--kernel-name` scoping: ```bash M=256 N=4096 K=8192 ncu --kernel-name 'regex:sm100_fp8_fp4_gemm' \ --launch-skip 3 --launch-count 1 --set full --target-processes application-only \ --export out -- python3 one_op.py # one_op.py warms the op 3x then loops; ncu skips the 3 warmups ``` 2. **`-- env VAR=v python3` defeats `--target-processes application-only`** - ncu profiles the `env` process, not its python child → 0 kernels. FIX: set the env on the ncu process itself (`VAR=v ncu … -- python3 …`), never via an `env` wrapper after `--`. 3. **TP=N MoE+sparse in-engine `--replay-mode application` is non-deterministic.** A large MoE + sparse-attention model relaunched per metric-pass varies its kernel-launch count → `==ERROR== Unexpected number of profiled kernels`, `--app-replay-mode relaxed` + no-op `profile_run` + prefix-caching-off + eager + fixed-blocks do NOT fix it. The canonical L4 path is an **isolated single-op kernel-replay harness**: call the production library op directly (e.g. `vllm.utils.deep_gemm.fp8_gemm_nt` / `tf32_hc_prenorm_gemm`), TP=1, **NO `mhc.tilelang` import**, synthetic fixed-shape inputs (AI/%SoL is shape-structural). Mirror the production caller's quant + SF layout (e.g. `per_token_group_quant_fp8` + `per_block_cast_to_fp8` + `deepgemm_post_process_fp8_weight_block`) so the exact kernel template fires. ## Two blockers on prod-direct attach An older `ncu --attach <PID>` recipe attached ncu to a running vllm process directly. That recipe is **broken** on current production deploys for two independent reasons. Read this section before attempting any live capture. ### Blocker 1: zymtrace already owns `CUDA_INJECTION64_PATH` Production vllm deploys instrumented with zymtrace run with: ``` CUDA_INJECTION64_PATH=/var/lib/zymtrace/profiler/libzymtracecudaprofiler.so ``` This is the zymtrace profiler agent injecting itself as the CUDA driver-level injection library. The CUDA driver only honors **one** injection library per process. Ncu cannot stack on top. There's no way to add ncu's `libcompute.so` to a running zymtrace-instrumented process. **Exception: latency/canary deploys often have NO zymtrace injection.** A canary that runs with no `CUDA_INJECTION64_PATH` in its env escapes Blocker 1 - ncu launch-mode (or the preload+TCP attach below) works directly on it without a separate sister-deploy. Check the target's env (`kubectl get deploy <name> -o jsonpath='{...env...}'`) before assuming a sister is required. The sister-deploy is only mandatory for the zymtrace-instrumented production releases. ### Blocker 2: ncu 2026.1.1 dropped `--attach <PID>` The deprecated `--attach <PID>` flag was removed in ncu 2026.x: ``` ==ERROR== unrecognised option '--attach'. Use --help for further details. ``` The modern ncu interaction modes (per `ncu --help`) are: - `--mode launch-and-attach` (default) - wrap the application at process startup - `--mode launch` - launch the application and suspend for a later attach - `--mode attach` - connect to a process pre-instrumented with ncu's injection library, via TCP (`--hostname` + `--port`) The only path that works against an **already-running** vllm process is `--mode attach`, and it requires `NV_NCU_INJECTION64_PATH` to have been set when vllm started so the process exposes the ncu profiling endpoint. Production vllm (instrumented with zymtrace instead) does not expose this endpoint. ## Recipe: sister-deploy with preload + TCP attach Both blockers are dodged by spinning up a parallel **sister-deploy** helm release running the same vllm image but with: - zymtrace `CUDA_INJECTION64_PATH` unset - `NV_NCU_INJECTION64_PATH` pointing at ncu's `libcompute.so` (mounted from the nsys-sidecar image via an initContainer + emptyDir volume) - `replicas: 1` - a Service selector / label that **excludes** prod traffic (the prod Service matches `app: basic-inference`, the sister uses `app: basic-inference-ncu`, so prod LB routes around it) The vllm process on the sister pod exposes ncu's TCP profiling endpoint on `127.0.0.1:49152`. From a `kubectl debug` sidecar with `--share-processes`, run: ```bash ncu --mode attach --hostname 127.0.0.1 --port 49152 \ --kernel-name '<KERNEL_REGEX>' \ --launch-count 5 \ --set full \ --target-processes all \ --export /profiling/ncu-$K.ncu-rep ``` Multiple captures against the same long-lived sister pod are supported - vllm stays warm. The sidecar runs ncu N times sequentially. ### Reference helm + capture scaffolding A reusable sister-deploy scaffold (one directory per model) contains: - `values-ncu-sister.yaml` - helm values overrides - `install.sh` - `helm upgrade --install` wrapper - `capture.sh` - full capture flow (wait-ready, warmup, sidecar attach, N-kernel loop, extract, helm uninstall). Flags: `--launch-count N`, `--set basic|full`, `--roofline-min` (AI-roofline `--metrics` set only), `--replay-mode kernel|application|range`, and `--campaign <slug> --cell-id <cell>` to auto-import the result into a perf-report campaign cell. - `README.md` - runbook + canonical 5-kernel list - a replay-mode-application runbook - the ack-gated path past the TP=8 NVFP4 `ContextSaveFailed` kernel-replay blocker (use `--replay-mode application --roofline-min`). ### Identifying hot kernels ncu `--kernel-name` accepts substring or regex match against the demangled or mangled kernel name. Demangle a mangled symbol via `c++filt` (available in the nsys-sidecar image): ```bash echo "_ZN66_GLOBAL__N__51f43292_25_CUDASymmetricMemoryOps_cu_c81adf72_277485226multimem_all_reduce_kernelIN3c108BFloat16ELi16EEEvPT_mPPjmm" | c++filt # -> (anonymous namespace)::multimem_all_reduce_kernel<c10::BFloat16, 16>(...) ``` Either form is fine for `--kernel-name`: ```bash --kernel-name multimem_all_reduce_kernel --kernel-name "regex:multimem_all_reduce.*BFloat16" ``` Canonical 5-kernel set for B200 NVFP4 inference (Kimi K2.6, GLM-5.1, DeepSeek): | Kernel substring | Category | Expected bottleneck | | --- | --- | --- | | `multimem_all_reduce_kernel` | NCCL | NVLink5 bandwidth | | `bmm_E2m1` | BMM-NVFP4 | NVFP4 Tensor Core compute | | `fmhaSm100fKernel` | FMHA | HBM3e bandwidth | | `nvjet_tst_.*_2cta` | MoE / GEMM | NVFP4 Tensor Core compute | | `triton_red_fused` | Triton-fused | Mixed | ## Recipe (deprecated - DO NOT USE on prod-direct) An earlier recipe used `ncu --attach $VLLM_PID` against the zymtrace-instrumented production pod. That recipe is preserved below ONLY as historical reference. It WILL FAIL on every current inference deploy. Use the sister-deploy section above. <details> <summary>Historical recipe (broken. Click to expand)</summary> ```bash # THIS WILL FAIL: # ERROR: unrecognised option '--attach' (ncu 2026.1.1) # ERROR: CUDA injection slot taken by zymtrace ncu --kernel-name '$KERNEL' --launch-count 5 --set full \ --export /profiling/ncu-$KERNEL.ncu-rep \ --attach $VLLM_PID ``` </details> ### 5. Export human-readable CSV ```bash kubectl -n $NS exec $TARGET_POD -c $SIDECAR -- bash -c " ncu --import /profiling/ncu-${KERNEL}.ncu-rep \ --csv --page raw > /profiling/ncu-${KERNEL}-raw.csv ncu --import /profiling/ncu-${KERNEL}.ncu-rep \ --csv --page details --section SpeedOfLight \ > /profiling/ncu-${KERNEL}-sol.csv " ``` Key columns from `--page details --section SpeedOfLight`: | Column | Meaning | |---|---| | `Memory Throughput [%]` | DRAM throughput vs theoretical peak | | `DRAM Throughput [%]` | Same, restricted to DRAM (vs L2) | | `Compute (SM) Throughput [%]` | SM utilization | | `Achieved Occupancy` | Actual warps-active / theoretical-max-warps-active | | `Theoretical Occupancy` | What the kernel COULD achieve given regs+smem | | `Achieved Active Warps Per SM` | Average warps active per SM | | `Block Limit Registers` | Limit imposed by registers/thread | | `Block Limit Shared Mem` | Limit imposed by smem/block | | `Block Limit Warps` | Limit imposed by max warps per SM | These are the roofline + occupancy diagnostics. A kernel with
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看