Skip to content

Profiling and compiler inspection

First run correctness tests. Keep profiling runs separate from latency runs because instrumentation/replay can change timing. Commands assume the corresponding NVIDIA tools are installed and results/local exists.

Compiler stages (works without hardware counters)

python scripts/inspect_kernel.py --op softmax --output results/local/ir
python scripts/inspect_kernel.py --op matmul --output results/local/ir

The script validates its result, then saves available TTIR, TTGIR, LLVM IR and PTX and reports registers, spills and shared memory. Find the reduction and masking in TTIR, distribution/layout choices in TTGIR, and load/store/math instructions in PTX. PTX is a virtual ISA; it is not the final SASS. The NVIDIA assembler and driver complete the path to hardware instructions. Exact IR names and structure depend on the Triton version.

System timeline, then kernel counters

On Linux/WSL with Nsight tools:

nsys profile --trace=cuda,nvtx --sample=none --cpuctxsw=none \
  --capture-range=cudaProfilerApi --output=results/local/softmax-timeline \
  python scripts/profile_kernel.py --op softmax
ncu --set full --profile-from-start off --launch-count 1 \
  --target-processes all --export results/local/softmax \
  python scripts/profile_kernel.py --op softmax

The script warms compilation/tuning before starting the profiler and marks a named NVTX range. For GEMM use --op matmul. Use the timeline to identify launch gaps, copies and unexpected extra kernels. Then inspect duration, DRAM throughput, L2 traffic, active warps, registers, spills, shared memory and stall reasons. High occupancy or a high cache hit rate alone does not prove a kernel is efficient.

Native CUDA on Windows (Developer PowerShell with tool directories on PATH):

compute-sanitizer --tool memcheck --error-exitcode 1 .\build\cuda_portfolio.exe --test-only
compute-sanitizer --tool racecheck --error-exitcode 1 .\build\cuda_portfolio.exe --test-only
ncu --set basic --kernel-name regex:softmax_parallel --launch-skip 6 --launch-count 1 .\build\cuda_portfolio.exe --json results\local\cuda-profiled.json

The skip selects the 1024x1024 validation launch after six small tests. Discard the timing JSON from profiled runs; it is not comparable to an uninstrumented run.

The Triton kernels deserve the same memory check. It runs natively in PowerShell with .venv-win activated, because the Windows CUDA Toolkit includes Compute Sanitizer (the WSL environment used here has no toolkit):

$env:PYTORCH_NO_CUDA_MEMORY_CACHING = "1"; $env:KERNEL_REQUIRE_GPU = "1"
compute-sanitizer --tool memcheck --error-exitcode 1 python -m unittest tests.test_gpu
Remove-Item Env:PYTORCH_NO_CUDA_MEMORY_CACHING

Turn PyTorch's allocator cache off while checking. Normally many tensors share one large CUDA allocation, so a write past the end of one tensor can land inside another and go unreported; with caching off, each tensor is its own allocation. The GPU tests check that inputs are unchanged but cannot see such a write; memcheck can. CUDA-graph capture needs the cache, so with it off the benchmark smoke tests switch from graph replay to event timing automatically, and the training-example tests (sixty steps of a small model, twice) are skipped as too slow. As a negative control, a copy kernel with its store mask removed on purpose was reported as Invalid __global__ write at the unmasked line, while the repository's GPU tests reported 0 errors (log).

For a fast check of every kernel path, including racecheck, use the smoke script. It runs each compiled variant once, in every dtype, and compares it with PyTorch, including every row plan and the backward kernels; under racecheck it finished in about three minutes here, while the full GPU test suite takes over an hour:

compute-sanitizer --tool racecheck --error-exitcode 1 python scripts/sanitizer_smoke.py

On Windows, if the sanitizer hangs after the tests finish, run the base interpreter instead of the virtual environment's launcher: set PYTHONPATH to src;.venv-win\Lib\site-packages and pass the base python.exe. The two launchers behave differently from run to run: the smoke script under memcheck ran fine through the virtual environment's launcher (a minute) but hung before starting through the base interpreter, so if one stalls, try the other. The last run reported 0 hazards (log).

On Linux with the CUDA Toolkit, the equivalent is PYTORCH_NO_CUDA_MEMORY_CACHING=1 KERNEL_REQUIRE_GPU=1 compute-sanitizer --tool memcheck --error-exitcode 1 python -m unittest tests.test_gpu.

Local limitation

Nsight Compute returned ERR_NVGPUCTRPERM on this machine: the driver does not grant hardware-counter access to this user. The diagnostic is retained in results/nsight-cuda.csv. No bandwidth/occupancy counter results are claimed and no system-wide driver permission setting was changed. CUDA event timings, Compute Sanitizer and compiler metadata remain useful independent evidence.

If counters are enabled later by the machine owner, rerun and add one observed counter-supported bottleneck plus one rejected hypothesis to the case studies.