fix(vllm): measure FPM self-benchmark decode points on a steady-state - #12358
Conversation
WalkthroughChangesDecode benchmark measurement
Estimated code review effort: 4 (Complex) | ~60 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/src/dynamo/vllm/tests/test_vllm_instrumented_scheduler.py (1)
2998-3000: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMove
import timeto module scope. Same applies to theimport timeinsidetest_two_step_group_skip_traverses_barrier_without_deadlock(Line 3135);timeis stdlib, so there's no collection-resilience reason to defer it.As per coding guidelines: "Keep imports at the top of the file; always flag
importstatements inside function bodies, methods, or classes as they hide dependencies and make modules harder to understand".♻️ Proposed change
def test_steady_step_unavailable_waits_out_the_deadline(): - import time - stub = InstrumentedScheduler.__new__(InstrumentedScheduler)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/src/dynamo/vllm/tests/test_vllm_instrumented_scheduler.py` around lines 2998 - 3000, Move the time import from test_steady_step_unavailable_waits_out_the_deadline and test_two_step_group_skip_traverses_barrier_without_deadlock to the module-level imports, removing both function-local imports while preserving the tests’ behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/src/dynamo/vllm/tests/test_vllm_instrumented_scheduler.py`:
- Around line 3131-3177: Add a pytest timeout marker to
test_two_step_group_skip_traverses_barrier_without_deadlock, using a duration
that covers its existing join timeout and cleanup. Keep the test logic unchanged
while ensuring a stalled ZMQ barrier cannot hang the suite indefinitely.
---
Nitpick comments:
In `@components/src/dynamo/vllm/tests/test_vllm_instrumented_scheduler.py`:
- Around line 2998-3000: Move the time import from
test_steady_step_unavailable_waits_out_the_deadline and
test_two_step_group_skip_traverses_barrier_without_deadlock to the module-level
imports, removing both function-local imports while preserving the tests’
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: afcbb6af-fd3e-4045-9b30-0580c5a03e5e
📒 Files selected for processing (2)
components/src/dynamo/vllm/instrumented_scheduler.pycomponents/src/dynamo/vllm/tests/test_vllm_instrumented_scheduler.py
This comment has been minimized.
This comment has been minimized.
tedzhouhk
left a comment
There was a problem hiding this comment.
One blocking data-grid issue inline; the steady-state timing approach otherwise looks good.
… step Every decode point admits its requests as brand-new, so the measured step ships full token arrays through scheduled_new_reqs (8.5 MB vs 6.8 KB per step at B=513) and pays first-step bookkeeping that production decode traffic never has after prefill. The database rows are systematically inflated -- up to 6.4x at B=256 (171.6 ms recorded vs 25.4 ms real) -- and the perf model is fitted to the bias. Measure the request's SECOND step instead: - Admit the batch one token short (max(1, ctx-1) per request) and dispatch one extra production-shaped step through scheduled_cached_reqs (_bench_make_steady_step: per-request delta, delay_cache_blocks like the admission injection, connector metadata like every sibling path). Record only the steady FPM. - Time the steady sample by the inter-update period: under async scheduling its schedule() runs while the admission step is still on the GPU, so the narrow schedule->output span would count that wait. - Validate the admission output against the injected lengths and the steady FPM against the coordinate the steady step actually reads; ctx=1 entries cannot give up a token, so clamped points are recorded at their measured coordinate with a context_clamped sample reason. - No new skip paths: a rank whose steady FPM never arrives sends its admission FPM through the existing collect_result barrier, where shape validation rejects it and every attention-DP rank skips the point together (regression-tested with a two-rank inproc pair). - The steady step deliberately adds no READY/GO round: production steps have no per-step barrier, and a ZMQ round between the two updates would inflate the measured inter-update time. Validation: full scheduler unit suite (133 tests, incl. 10 new steady-state tests) executed against vLLM 0.25.1 in-container. Prototype A/B on MiniMax-M2.7-NVFP4 4xB200: B=256 171.62 -> 26.71 ms vs 25.36 ms measured end-to-end; B=1 4.71 ms vs 4.83 ms real. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: YZLi <yuanli@nvidia.com>
A stalled ZMQ barrier would otherwise hang the suite; pytest-timeout covers the join timeout plus synchronizer cleanup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: YZLi <yuanli@nvidia.com>
…gnment Generated decode presets below 2*batch_size all measure at 2*batch_size (their partitions only contain ctx 1 and 2 entries, which the admission clamp makes indistinguishable), so a generated grid could carry duplicate rows at one coordinate and overweight the shortest context in the perf-model fit. Map every preset to the coordinate its steady step actually measures and dedupe before benchmark IDs and the grid digest are assigned. The runtime clamp and context_clamped tagging remain as a safety net for explicit user-specified points only. Signed-off-by: YZLi <yuanli@nvidia.com>
83fbe8b to
cf7a11d
Compare
…ization Steady-coordinate normalization (ai-dynamo#12358) merges each batch's sub-2B decode presets into a single point, shrinking the synthetic negotiation grid from 1368 to 1266 points and moving batch=1's feasibility boundary from index 19 to 18. Signed-off-by: YZLi <yuanli@nvidia.com>
Overview:
Every FPM self-benchmark decode point admits its requests as brand-new, so the
measured step ships full token arrays through
scheduled_new_reqs— 8.5 MBvs 6.8 KB per step at B=513, plus first-step bookkeeping that production
decode traffic never pays after its prefill. The recorded rows are
systematically inflated (up to 6.4x: 171.62 ms recorded vs 25.36 ms
measured end-to-end at B=256 on MiniMax-M2.7-NVFP4, 4xB200), and the perf
model downstream is fitted to the bias. This is the mechanism behind the
"more self-benchmark points somehow degrade real-traffic prediction"
observation in the AIC forward-pass evaluation: the contaminated y-values,
not the point distribution.
This PR measures each decode point on the request's second step — a
production-shaped steady-state decode iteration.
Details:
(
max(1, ctx-1)per request) and one extra step is dispatched throughscheduled_cached_reqs(_bench_make_steady_step, mirroring the referencescheduler's running-request branch: per-request delta payload,
delay_cache_blockslike the admission injection, kv/ec connector metadatalike every sibling path). Only the steady FPM is recorded; the admission
FPM is scaffolding and is dropped at save time.
scheduling its
schedule()runs while the admission step is still on theGPU, so the narrow schedule->output span would also count that wait
(measured: B=1 read 11.29 ms ≈ 6.74 + 4.83 with the narrow window, 4.71 ms
with the inter-update period vs 4.83 ms real). The gate fails closed to
the narrow window if the two updates are ever not adjacent.
the actually injected lengths; the steady FPM against the coordinate the
steady step actually reads.
ctx=1entries cannot give up a token, soclamped points are recorded at their measured coordinate with a
context_clampedsample reason (this is also what makes the safety network — an admission-only FPM can never masquerade as a valid measurement).
never arrives sends its admission FPM through the existing
collect_resultbarrier, where shape validation rejects it and everyattention-DP rank skips the point together. Regression-tested with a
two-rank inproc synchronizer pair (one rank short, no deadlock, identical
group decision).
decode steps have no per-step barrier either, and a ZMQ round between the
two updates would be counted into the steady step's inter-update wall_time
under synchronous scheduling. Group agreement is enforced at
collect_result.(the steady step neither returns
new_token_idsfor non-last PP stages norhonors the pp_size decode cadence), and the feasibility bound is
clamp-aware (
max(ctx,2)+2 <= max_model_len), fixing an engine-killingassert at the degenerate
max_model_len==3configuration.measurement_policyblock(
decode: steady_state_second_step) so database provenance is explicit.Validation
and the DP-barrier regression test — executed in-container against
vLLM 0.25.1; vLLM 0.26.0 API compatibility (CachedRequestData fields,
allocate_slotskwargs,num_output_placeholders) verified against the0.26.0 sources.
finding independently re-verified): all confirmed findings fixed in this
commit; 9 of 14 initial claims were refuted with code evidence.
Composition note for #12176 (eager-warmup)
The two PRs are textually independent (verified: this diff applies to main
with zero overlap against #12176's hunks). Whichever merges second should
handle two semantic touch points: warmup replicas will also run two-step
(desired — identical execution keeps DP grids rank-identical), and a warmup
replica that fails steady validation must be discarded, not recorded in
skipped_points(evaluateEAGER_WARMUP_REASONbefore converting avalidation failure into a skip).
Where should the reviewer start?
components/src/dynamo/vllm/instrumented_scheduler.py:_bench_make_steady_step— the production-shaped step (docstring carriesthe payload rationale)
_bench_step_decode— ctx-1 injection, coordinate rewrite, steadydispatch, deadline flow
_bench_steady_fpm_expected+ the timing branch in_update_from_output_bench_output_validation_error/_bench_save_current_point— theadmission-vs-steady expectations and the save-time trim
components/src/dynamo/vllm/tests/test_vllm_instrumented_scheduler.py—the "Steady-state decode measurement" block, especially
test_two_step_group_skip_traverses_barrier_without_deadlockRelated Issues
🚫 This PR is NOT linked to an issue:
Summary by CodeRabbit
New Features
Bug Fixes
Tests