Repository navigation
fix(profile): replay captured graphs node by node on every ROCm line so a trace holds the kernels of every decode replay - #2144
Conversation
…e kernels of every decode replay With the HIP runtime's default replay of a captured graph (from its recorded packets) the ROCm profiler records a replay's kernels only for the replay that a host sync follows: the profiler's stop, or a prefill step. Measured on one Qwen3-14B-FP8 SGLang server (764-kernel decode replays, concurrency 64, 1k/1k): a 128-step window had the kernels of 1 of 127 replays, a 32-step window 1 of 32, and single-step by-stage windows had none in 3 of 3. A profile's op shares were therefore those of the eager ops around the replays and of its prefill steps, not of the decode steps the graded workload runs. The profile configurations now set DEBUG_CLR_GRAPH_PACKET_CAPTURE=0, so the runtime replays a graph node by node. On the same server and load: 128 of 128 replays recorded in a 128-step window, 32 of 32 in a 32-step one, and 4 of 4 single-step windows, the kernel names in capture order. Decode throughput at concurrency 64 is unchanged within noise while windows were profiled (median 5277 against 5208 tok/s). The baselines do not carry it. The variable is a HIP runtime setting, independent of the framework, so the vLLM and ATOM profile configurations carry it too; it was measured on SGLang only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Code metrics gate: PASSEDJudged: the 4 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.
22 findings in files this change does not touchOutside this change (informational): 22
Thresholds and sources: |
chaojhou
left a comment
There was a problem hiding this comment.
PR #2144 -- fix(profile): replay captured graphs node by node so a trace holds the kernels of every decode replay
What it does: sets DEBUG_CLR_GRAPH_PACKET_CAPTURE: "0" in the shipped profile_sglang.yaml, profile_vllm.yaml and profile_atom.yaml. The goal is for the HIP runtime to replay a captured graph node by node while a profile runs, so the ROCm profiler records the kernels of every decode replay instead of only the replay a host sync follows. On every single-node path that uses these files, the variable reaches the server process: Magpie exports benchmark.envs into the launch shell, or as docker run -e, and the reuse fingerprint keeps a baseline-booted server from being reused. Profile throughput is never graded, so a slower replay mode cannot skew KEEP/REVERT.
Blocking issues: 1
- [X2] The variable does not exist in the HIP runtime of the repo's validated ROCm stacks, so on those stacks the change does nothing, while the description and comments present it as unconditional [verified]
Problem:DEBUG_CLR_GRAPH_PACKET_CAPTUREis defined in ROCm CLR'sprojects/clr/rocclr/utils/flags.hppinrocm-7.2.0throughrocm-7.2.3. It is absent atrocm-7.2.4,release/therock-10.0anddevelop, where the segmented graph executor replaced packet capture.docs/compatibility.rst:125,155-159names ROCm 10.0 (rocm10images) as the validateddockerstack for SGLang and vLLM, and SGLang bare metal on 7.2.x resolves the 7.2.4 layer (:226). On those runtimes the new entry (src/hyperloom/inference_optimizer/assets/configs/profile_sglang.yaml:65, profile_vllm.yaml:77, profile_atom.yaml:72) is silently ignored. The description's measurement comes from one ROCm 7.2.0 SGLang image, but its observable-effect line and the YAML comments state the effect without a version bound ("A HIP runtime setting, independent of the framework ... Replayed node by node (0) every replay is recorded").
Impact: on the default stacks a profile records exactly what it recorded before. If the missing-replay symptom also exists under ROCm 10's executor, decode kernels are still missing while the release notes say they are fixed. If it does not exist there, the comments describe a mechanism those runtimes do not have.
Action: measure the missing-replay symptom on a ROCm 10.0 image and on 7.2.4. If it reproduces, find that runtime's own switch (or report it upstream alongside ROCm/rocm-systems#13882) instead of shipping a variable it ignores. If it does not reproduce, scope the change: the YAML comments and the observable-effect line should say the variable applies to HIP runtimes up to ROCm 7.2.3 and is ignored on 7.2.4 and ROCm 10.
Checked: the three YAML hunks and the new test; ProfileExecutor config selection (profile.py _default_profile_config), materialize_config_with_envs, the native AgentX path (magpie_agentx.enable_profile, which never reads these files) and the multi-node restart path (which forwards only reference-launch envs); Magpie 6a1ee26f config.get_env_vars, benchmarker.py local/docker env export and reuse fingerprint, and the *_mi300x.sh launchers; consumers of profile throughput (writeback.py profile path, audit extras, roofline anchor); docs/compatibility.rst stack versions | Ran: gh api for ROCm/rocm-systems projects/clr/rocclr/utils/flags.hpp at rocm-7.2.0, 7.2.3, 7.2.4, release/therock-10.0 and develop (the flag is present only through 7.2.3); test_profile_replay_config.py at head (3 passed) and with the sglang YAML reverted (fails); gh pr checks 2144 (35 pass, 7 skipped); two independent refutation passes per finding | Base: 5c33315 | Head: 6ea73ec
…-graph-packet-capture
…sion DEBUG_CLR_GRAPH_PACKET_CAPTURE exists only in the HIP runtime of ROCm 7.1.0-7.2.3. ROCm 7.2.4 replaced it with the segmented executor (DEBUG_HIP_GRAPH_SEGMENT_SCHEDULING=0 turns it off, and with it packet capture), and ROCm 10.x selects the classic executor, which does not capture packets, with DEBUG_HIP_GRAPH_CLASSIC_PATH=1. A runtime ignores the switches it does not define, so the profile configurations set all three; the comments state the version range of each and that only the 7.2.0 setting is measured. Co-Authored-By: Claude <noreply@anthropic.com>
|
@chaojhou Thanks, agreed: on 7.2.4 and 10.x the variable alone did nothing. Each later runtime has its own switch to the non-captured path, so 37db4c5 sets all three (a runtime ignores the ones it does not define) and scopes the comments and the description by version:
Not measured, and stated so in the YAML comments and the description: whether the missing-replay symptom reproduces on 7.2.4 / 10.x and whether these switches restore every replay there. Only the 7.2.0 setting has GPU numbers. Test: |
Fixed in 37db4c5 (each ROCm line's switch set and the effect scoped by version); superseded by the LGTM comment below.
PR #2144 -- fix(profile): replay captured graphs node by node so a trace holds the kernels of every decode replayWhat it does: sets the HIP runtime's switch for the non-captured, per-node graph replay in the shipped Blocking issues: none The finding from my review at 6ea73ec is fixed in 37db4c5. The repo's validated stacks (ROCm 10.0 images, 7.2.4 bare metal) now get their own runtime's switch. I checked each flag in Checked: the three YAML hunks and the test at 37db4c5 against a merge of main; ROCm CLR LGTM |
chaojhou
left a comment
There was a problem hiding this comment.
Approved for merge; see the LGTM comment above.
Summary
The shipped profile configurations (
profile_sglang.yaml,profile_vllm.yaml,profile_atom.yaml) now set the HIP runtime's switch that replays a captured graph node by node instead of from pre-recorded AQL packets while a profile runs. The switch differs per ROCm line, and a runtime ignores the ones it does not define, so all three are set:projects/clr)rocm-7.1.0..rocm-7.2.3(andrelease/therock-7.9,7.10)DEBUG_CLR_GRAPH_PACKET_CAPTURE=0(flags.hpp@rocm-7.2.3)rocm-7.2.4(andrelease/therock-7.11..7.14)DEBUG_HIP_GRAPH_SEGMENT_SCHEDULING=0(flags.hpp@rocm-7.2.4)Runtakes the classicRunNodespath (Run)release/therock-10.0..10.2,developDEBUG_HIP_GRAPH_CLASSIC_PATH=1(flags.hpp@therock-10.0)GraphExecClassicinstead ofGraphExecSegmented(hip_graph.cpp): "classic topological enqueue, no AQL capture" (GraphExecClassic)On 10.x
DEBUG_HIP_GRAPH_SEGMENT_SCHEDULING=0is that runtime's default (hybrid stream assignment), so the 7.2.4 entry changes nothing there.GraphExecClassicrefuses multi-device graphs; the frameworks' per-rank decode graphs are single-device.With the runtime's default replay (from the graph's recorded packets) the ROCm profiler records a replay's kernels only for the replay that a host sync follows: the profiler's stop, or a prefill step. Measured on one Qwen3-14B-FP8 SGLang server (764-kernel decode replays, concurrency 64, ISL/OSL 1k/1k): a 128-step window had the kernels of 1 of 127 replays, a 32-step window 1 of 32, a 4-step by-stage window 1 of 4, and single-step by-stage windows had none in 3 of 3. A profile's op shares were therefore those of the eager ops around the replays and of its prefill steps, not of the decode steps the graded workload runs.
Replayed node by node, on the same server and load: 128 of 128 replays recorded in a 128-step window, 32 of 32 in a 32-step one, 4 of 4 in a 4-step by-stage window and 4 of 4 single-step windows, with the kernel names in capture order and no misreported eager event. Decode throughput at concurrency 64 is unchanged within noise while windows were profiled (median 5277 against 5208 tok/s). The window setting is unchanged: the long window keeps its prefill steps, and every decode replay in it now counts.
The baselines do not carry the variable, so a measured run replays as shipped.
New
test_profile_replay_config.pypins, per framework, that the profile carries it and the baseline does not.Not measured
profile_sglang.yamlrendered by the multibranch measurement backend; this change was not re-run throughProfileExecutorand Magpie on a GPU.Test plan
pytestontest_profile_replay_config.py(9: 3 frameworks x 3 switches),test_profile_and_kernel_handlers.py,test_custom_framework.py,test_profile_gpu_preflight.py,test_profile_startup_shim.py: 225 passed; the one failure,test_trace_analyze_handler_dry_run_returns_structured_result, fails the same way onmainin my environment (TraceLensis not installed there).DEBUG_HIP_GRAPH_CLASSIC_PATHfromprofile_vllm.yamlturns that case red (KeyError: 'DEBUG_HIP_GRAPH_CLASSIC_PATH'), the other 8 stay green.ruff check,ruff format --check(0.16.2),scripts/code_metrics.py --base-ref origin/main(PASSED).Review template
🤖 Generated with Claude Code