analyze-nsys-profile

star 197

Query a captured PithTrain Nsight Systems profile to measure compute/communication overlap, locate exposed comm by DualPipeV stage, and inspect per-rank stream behavior. Use when the user asks to "analyze an nsys profile", "check overlap quality", "find exposed comm", "which stage is the bottleneck", or any question that starts from an existing `.nsys-rep` file. Assumes the trace was already captured (see capture-nsys-profile); provides query primitives the agent composes for the specific question being asked.

mlc-ai By mlc-ai schedule Updated 6/13/2026

name: analyze-nsys-profile description: Query a captured PithTrain Nsight Systems profile to measure compute/communication overlap, locate exposed comm by DualPipeV stage, and inspect per-rank stream behavior. Use when the user asks to "analyze an nsys profile", "check overlap quality", "find exposed comm", "which stage is the bottleneck", or any question that starts from an existing .nsys-rep file. Assumes the trace was already captured (see capture-nsys-profile); provides query primitives the agent composes for the specific question being asked.

Analyze Nsys Profile

A passive query toolkit for PithTrain nsys traces. The agent asks a specific question; the skill provides primitives that answer it fast and correctly. The skill does not produce an unsolicited full report. It expects the agent to compose the right query for the question being asked.

Prerequisites

  • A captured .nsys-rep exists (default location: workspace/capture-nsys-profile/pithtrain_node*.nsys-rep).
  • The repo venv is active: source .venv/bin/activate.
  • nsys CLI on PATH (for the one-time SQLite export).

Step 1 — Export the trace to SQLite

nsys export --type=sqlite --force-overwrite=true --output=workspace/capture-nsys-profile/pithtrain_node0.sqlite workspace/capture-nsys-profile/pithtrain_node0.nsys-rep

All subsequent queries hit the SQLite, not the raw .nsys-rep.

Step 2 — Shared preparation (always run first)

Three primitives establish who, when, and what — every downstream analysis depends on the data they surface. Run (or at least understand the output of) all three before reaching for the analysis scripts below.

Question Primitive
What ranks are in this trace? What's the per-rank setup? show_setup.py
What's the steady-state analysis window for each rank? find_window.py
Which streams are compute / comm, and what's each comm stream's purpose? classify_streams.py

Pipeline: show_setupfind_windowclassify_streams. show_setup gives you the mapping pid ↔ rank ↔ mesh coordinates; find_window picks the median DualPipeV chunk per rank (deterministic across re-runs, so before/after comparisons are valid); classify_streams identifies which CUDA streams in that window are compute vs comm, and labels the comm streams' purpose (ep_a2a, cp_ring, pp_p2p).

Step 3 — Measure overlap per DualPipeV stage

python .agents/skills/analyze-nsys-profile/scripts/compute_overlap.py workspace/capture-nsys-profile/pithtrain_node0.sqlite

Emits one row per (rank, stage) with columns: pid | stage | exposed_ns | overlap | overlap_min | overlap_max. The overlap column is the time-weighted hidden fraction across the stage's comm kernels; overlap_min / overlap_max are the extremes of the per-kernel overlap percentage and surface whether the stage is uniformly bad or bimodal.

See references/examples.md for recipes that compose this with the Step 2 primitives.

Critical conventions

Before composing a custom SQL query, read references/conventions.md. Highlights:

  • pid (Linux PID) is the per-rank join key, extracted exactly as the nsys docs prescribe: globalPid / 0x1000000 % 0x1000000 (kernel rows) == globalTid / 0x1000000 % 0x1000000 (NVTX rows). Single SQLite per node → PIDs unique within a trace.
  • Always filter start >= 0 — pre-cudaProfilerStart NCCL init ranges have negative timestamps.
  • Per-rank setup label is the first in-window NVTX event per rank: rank=N; pp=R/S dp=R/S cp=R/S ep=R/S; mbs=M seq=Q.
  • Chunk anchor for steady state: the median-indexed forward chunk X (phaseY) backward chunk Z (phaseW) NVTX range emitted by DualPipeV. Match with LIKE 'forward chunk%backward chunk%' to disambiguate from per-stage forward markers.
  • Compute-vs-comm classification: a kernel is communication if its short name starts with nccl, otherwise compute. A stream is a comm stream iff every one of its kernels is NCCL.
  • Comm-stream purpose is discovered from the PithTrain stage-NVTX (layer*.stageN_*) enclosing each kernel at its CPU-side launch time — every kernel must agree on the label (unanimity), otherwise mixed.

Non-fragile classification rules

Avoid these heuristics — they break across configs:

  • "Stream with > N kernels of type X is comm" (N depends on layer count, chunks, seq length).
  • "Kernel duration > T µs means data movement" (long duration can also be a straggler wait).
  • "Stream with avg µs < threshold is EP" (depends on token volume per rank).

Use these instead:

  • One-sided purity check for compute-vs-comm streams.
  • NVTX-context labeling for stream purpose: look up the innermost PithTrain stage range enclosing each kernel and require unanimous agreement. Implemented in classify_streams.py.

Worked examples

See references/examples.md for recipe-style answers to:

  • How well is each EP phase overlapped with compute?
  • Which EP phase has the worst overlap?
  • Are the PP stages balanced?
  • Which (rank, stage) carries the most exposed comm?

Output guidance

  • Scripts emit a fixed-width table to stdout. One column per record field; agents read it directly.
  • Cross-script joins are by pid — every script's table includes pid as the per-rank identifier; downstream rows compose against show_setup's mapping pid ↔ rank ↔ setup.
  • When reporting to the human user, summarize as plain prose with a small table extracted from the relevant columns.
  • Always cite the analysis window. An overlap percentage with no window is meaningless.

Gotchas (surfaced by prior agent runs)

  • classify_streams.py only reports streams active in the analysis window, not every stream that exists in the trace. A rank typically has 6-8 streams overall but only 2-3 inside a single steady-state chunk. This is intentional — analyzing a small window does not need the inactive streams.
  • PP P2P kernels rarely appear in a single chunk window — they fire between chunks. Widen the window (--start NS --end NS on the analysis script) if you specifically want to see the PP P2P comm stream.
  • compute_overlap.py's percent cells include a trailing % (58.4%, not 0.584). Sort/compare numerically by stripping the % first. Absolute time columns (exposed_ns) are bare integer nanoseconds.
  • CPU launch time vs GPU execution time — for any NVTX-context lookup on a kernel, use kernel["launch_start"] (CPU-side cudaLaunchKernel time) rather than kernel["start"] (GPU-side execution time). All scripts already do this; if you write an ad-hoc query, call common.innermost_nvtx on launch_start values.
  • Comm-stream purpose uses unanimity, not majority — every kernel on the stream must agree on its enclosing-NVTX category, otherwise the label is mixed. A single mis-categorized kernel surfaces as mixed instead of silently being out-voted.

Common Issues

no such table: NVTX_EVENTS

The .nsys-rep has not been exported yet. Run the nsys export command from Step 1.

PP P2P comm is missing from the overlap output

By design. compute_overlap.py buckets kernels by their enclosing PithTrain stage NVTX (stage1_* through stage5_*); PP P2P kernels live inside the pipeline send/recv wrapper, which is not a stage marker, so they are filtered out. Widen the window with --start NS --end NS if you need to investigate them — they typically fire between chunks, not inside.

Negative timestamps on NVTX events

NCCL init opened these ranges before cudaProfilerStart. Filter WHERE start >= 0 to scope to the profiled window.

Install via CLI
npx skills add https://github.com/mlc-ai/pith-train --skill analyze-nsys-profile
Repository Details
star Stars 197
call_split Forks 16
navigation Branch main
article Path SKILL.md
More from Creator