Skip to content

Nsight Compute Kernel Profiling: From Bottleneck Detection to Kernel Tuning ​

Note: This is a review article (no first-hand experimental data), based on public documentation and common tuning practices.

Experiment Objective ​

Earlier articles built cluster-level observability (ch02 DCGM) and single-card bandwidth baselines (ch01), but the root of slow training usually lives in one or two operator kernels. The goal: upgrade profiling from "guess by watching nvidia-smi" to "locate the kernel bottleneck with Nsight Compute" — master the three-phase profiling method (which kernel is slow → what limits it → warp-state details), Speed of Light, and the four bottleneck classes (throughput/latency/occupancy/tail effects), plus the tuning action for each. After reading, you can answer "why is this kernel slow" with evidence.

Code Walkthrough ​

Part 1: Three-phase profiling commands ​

bash
# Phase 1: find "which kernel costs the most time" (Nsight Systems, low overhead)
nsys profile -o train_profile --stats=true \
  python train.py --batch 32

# Phase 2: full profile of the target kernel (Nsight Compute, heavy; filter only)
ncu --kernel-name regex:matmul \
    --launch-count 3 --launch-skip 10 \
    -o kernel_prof python train.py --batch 32

# Phase 3: Speed of Light overview and bottleneck judgment
ncu --section SpeedOfLight \
    --section Occupancy \
    --section WarpStateStats \
    -o sol_prof python train.py --batch 32

Key points:

  1. Nsight Systems is the "map"; Nsight Compute is the "microscope": nsys overhead is tiny — run it fully to find the most expensive kernels; ncu profiling is heavy (possibly 10× slower), so use --kernel-name to filter to target kernels only, never profile everything.
  2. --launch-skip 10 / --launch-count 3: GPU kernel launches have warmup effects (first allocations, compile cache); skip the first 10 launches and profile 3 steady-state ones.
  3. The three sections are the judgment core: SpeedOfLight (theoretical vs measured throughput), Occupancy (SM occupancy), WarpStateStats (what warps are waiting for — memory, sync, or nothing to do).

Part 2: Scripted bottleneck classification (simplified) ​

python
def classify_bottleneck(sol):
    mem_util   = sol['Memory Throughput %']
    compute_util = sol['Compute (SM) Throughput %']
    achieved_occ = sol['Achieved Occupancy']
    warp_stall  = sol['Stall Long Scoreboard']

    if mem_util > 85 and compute_util < 60:
        return 'MEMORY_BOUND', 'reduce memory traffic: vectorize, reuse shared memory, float4 loads'
    if compute_util > 85 and mem_util < 60:
        return 'COMPUTE_BOUND', 'reduce compute: lookup tables, approximations, better algorithms'
    if achieved_occ < 0.5:
        return 'LATENCY_BOUND', 'raise occupancy: fewer registers/shared memory, more parallelism'
    if warp_stall > 0.5:
        return 'LATENCY_BOUND(stall)', 'hide memory latency: more in-flight requests, prefetch'
    return 'BALANCED', 'no obvious bottleneck; check tail effects and scheduling overhead'

Principles ​

Speed of Light and the four bottleneck classes ​

The Speed of Light report compares measured kernel throughput against hardware theoretical peaks:

  • Memory Throughput %: actual SM memory traffic / theoretical bandwidth ceiling. Near 90%+ means the kernel is "memory-fed full" — adding compute does nothing; reduce memory traffic.
  • Compute (SM) Throughput %: instruction throughput / theoretical compute ceiling. Near the ceiling means "compute-fed full" — reduce compute or change algorithms.
  • Achieved Occupancy: actual occupancy = active warps / theoretical max. Low occupancy (<50%) means too little parallelism on the SM for latency hiding.
  • Warp State: distribution of warp stall reasons — Long Scoreboard (waiting on memory), Barrier (waiting on sync), Wait (dependency). The "why" of a bottleneck only shows up here: two kernels both at 80% throughput, one stuck on memory waits and the other on barrier sync — completely different tuning directions.

Three typical bottlenecks and actions ​

BottleneckSignatureTuning action
Memory-boundMem% high + Compute% lowvectorize (float4), shared-memory tiling reuse, coalesced access, L2 hit optimization
Compute-boundCompute% high + Mem% lowfewer instructions: approximations/lookup, remove redundant work, algorithmic optimization
Latency/occupancyOccupancy < 50%, high stallsfewer registers, less dynamic shared memory, more independent parallelism (ILP)

Key insight: kernel tuning is "qualify first, quantify second" — judge the bottleneck class first (qualitative), then act within that class (quantitative). Skipping the qualification and tuning parameters directly often fine-tunes in the wrong direction.

Division of labor with cluster monitoring ​

Ch02 DCGM is "what all cards in the cluster are doing"; this article's Nsight is "why this kernel on this card is slow." Practical chain: DCGM detects abnormal training throughput on a node → locate the low-utilization operator → Nsight profiles that kernel → fix and regression-compare. A single-operator optimization often yields 20%-50%, but only after monitoring "pulls it out" does profiling have a target.

Results ​

(Estimated, not measured; typical optimization examples)

Tuning actionTypical gainApplicable bottleneck
float4 vectorized loads1.5-2×memory-bound
shared-memory tiling1.3-1.8×memory-bound (repeated global reads)
fewer registers to raise occupancy1.2-1.5×latency/occupancy
remove redundant instructions / algorithm swap1.3-2×compute-bound

Engineering Takeaways ​

  1. Map first, microscope second: always use nsys to find "the kernel worth profiling" before ncu. Direct full ncu profiling is the worst time-to-output ratio.
  2. Read the three sections together, not one metric: high Mem% is not automatically a memory bottleneck — check Occupancy and Warp State too; judge jointly to avoid misdiagnosis.
  3. Put profiling in the delivery flow: performance-sensitive kernels should keep ncu reports as baselines and compare on regression — "this version did not regress" needs evidence, not feeling.
  4. Optimize the biggest head first: sort profile results by time and chew the largest kernel first; small-kernel gains contribute little to total time.

Next article preview: profiling found "where the kernel is slow"; the next layer is "why the kernel is written this way" — "PTX and SASS: Intermediate Representations and Tuning Methods for GPU Kernels."

AI-HPC Expert · Contact: openaihpc@gmail.com