Conversation
SumanthRH
added this pull request to stack #2400
October 4, 2026 02:40
This was referenced Oct 4, 2026
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 05:03
a3cf1e6 to
04290d6
Compare
SumanthRH
added a commit
that referenced
this pull request
Oct 4, 2026
#2364) # What does this PR do? Add vLLM preemption, external prefix-cache, KV reload and request-latency metrics to SkyRL's existing step collection. TLDR: Collect the launched servers' metrics, reduce counter and histogram deltas, and use request TPOT for `tpot_seconds_avg`. ## Why this is needed The existing scraper does not report preemptions or external-cache/offload activity, and its TPOT field uses inter-token latency. These gaps make it difficult to compare cache configurations alongside training throughput. Unfiltered cluster samples can also include other deployments. ## How it works - Add `VLLMServerActor.get_ray_worker_id()` and resolve the API server actors' WorkerIds during setup. Filter samples to those workers. The lookup has a 10-second timeout; failure disables collection without stopping training. External deployments without owned actors retain unfiltered collection. - Sum engine counters before taking deltas. Merge compatible classic-histogram buckets before interpolating P90. Failed endpoint scrapes discard partial snapshots; missing baselines and invalid latency windows are omitted. A missing bucket baseline is treated as zero only when the previous histogram count is explicitly zero. - Log preemption counts and preemptions per million output tokens, external hit rate, KV reload bytes/s, and TTFT/request TPOT mean and P90. TPOT uses `request_time_per_output_token_seconds`. ITL, P50 and offload store throughput remain available as raw metrics for Grafana. Sync keys retain `vllm/train/*` and `vllm/eval/*`; fully async keys retain `vllm/*`. Collection uses the existing `generator.inference_engine.enable_ray_prometheus_stats` flag. ## Validation Collector tests cover worker filtering, bounded identity lookup, partial HTTP failures, counter resets, latency formulas and lazy histogram buckets. API-server source and historical actor PID/worker/export records confirm that the lookup returns the WorkerId used by vLLM's Ray metrics logger. Combined-stack CPU check at `04290d61` (#2401): **70 tests passed**. `--noconftest` skips the repository's automatic Ray lifecycle fixture so these mock/CPU checks do not attach to the live cluster. Ray 2.58.0 matches the validation cluster. ```bash uv run --isolated --extra skyrl-train --extra dev --with ray==2.58.0 pytest --noconftest \ tests/train/test_grafana_annotations.py tests/train/test_metrics_lifecycle.py \ tests/train/test_vllm_run_statistics.py tests/train/test_vllm_metrics_scraper.py \ tests/train/test_vllm_window_statistics.py tests/train/test_vllm_engine_imbalance.py \ tests/train/test_vllm_pd_metrics.py tests/train/test_tracking.py -q ``` No new GPU benchmark was run for this layer. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes how TPOT and percentiles are defined for logged metrics and adds setup-time Ray worker identity resolution; failures are handled by disabling collection, but mis-scoped or missing metrics could affect monitoring during training. > > **Overview** > Expands step-level vLLM metrics logged to the trainer tracker and **scopes scraping to SkyRL-launched inference servers** by resolving each `VLLMServerActor`'s Ray `WorkerId` at setup (10s timeout; lookup failure disables collection without aborting training). > > The scraper now emits **preemptions**, **external prefix-cache hit rate**, **KV offload load throughput**, and **TTFT/request-TPOT averages plus P90** (merged histogram bucket deltas via new `vllm_window_statistics`). **`tpot_seconds_avg` switches from inter-token latency to `request_time_per_output_token_seconds`**, so it is not comparable to older runs. Partial node scrape failures drop the whole snapshot and reset the delta baseline. > > Docs and tests cover worker filtering, incomplete snapshots, and the new reduction paths. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1e2064c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Signed-off-by: Codex <codex@users.noreply.github.com> Signed-off-by: SumanthRH <sumanthrh99@gmail.com> Co-authored-by: Codex <codex@users.noreply.github.com>
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 05:31
04290d6 to
59a2eab
Compare
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 05:35
59a2eab to
e700214
Compare
SumanthRH
added a commit
that referenced
this pull request
Oct 4, 2026
# What does this PR do? Report how evenly inference engines share prompt and output generation work. TLDR: Add aggregate coefficients of variation (CV) for per-engine token deltas, including observed idle engines. ## How it works Group token counters by WorkerId and engine index, then compare each engine's deltas over the sampling interval. CV is population standard deviation divided by mean; a common time denominator cancels from this ratio. Emit `prompt_throughput_cv` and `generation_throughput_cv`, plus a `_num_engines` field for each. Engines observed at both boundaries contribute; invalid/reset deltas are excluded. Omit CV for fewer than two contributing engines or zero mean. Sync windows require a valid start snapshot before deriving CV or engine counts; failed baselines leave only the current gauges. The training logger receives aggregate scalars. ## Validation Focused cases check idle-engine inclusion, equal load, missing engines and zero work. Combined-stack CPU check at `04290d61` (#2401): **70 tests passed**. `--noconftest` skips the repository's automatic Ray lifecycle fixture so these mock/CPU checks do not attach to the live cluster. Ray 2.58.0 matches the validation cluster. ```bash uv run --isolated --extra skyrl-train --extra dev --with ray==2.58.0 pytest --noconftest \ tests/train/test_grafana_annotations.py tests/train/test_metrics_lifecycle.py \ tests/train/test_vllm_run_statistics.py tests/train/test_vllm_metrics_scraper.py \ tests/train/test_vllm_window_statistics.py tests/train/test_vllm_engine_imbalance.py \ tests/train/test_vllm_pd_metrics.py tests/train/test_tracking.py -q ``` The existing HTTP503 reproducer confirms that a failed sync baseline returns current gauges without CV or engine counts. All 29 existing collector, histogram and imbalance tests also passed on this branch. No tests were added or edited, and no GPU benchmark was run. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Observability-only additions to metrics aggregation with guarded CV math; no changes to inference or training control flow. > > **Overview** > Adds **per-engine load imbalance** signals to vLLM Ray metrics scraping by tracking prompt and generation token counters per `(WorkerId/ReplicaId, engine)` and emitting aggregate **coefficients of variation** over each sampling window. > > `VLLMMetricsScraper` now builds an `_engine_snapshot` on each scrape (including idle engines seen via `num_requests_running`), keeps previous/window baselines, and merges `prompt_throughput_cv`, `generation_throughput_cv`, and matching `_num_engines` into both step `sample()` output (`vllm/…`) and explicit `start`/`stop` windows (`{label}/…`). CV is computed from non-negative token deltas across engines present at both boundaries; it is omitted when fewer than two engines contribute or mean delta is zero. > > New unit tests cover idle-engine inclusion, equal load (CV=0), and missing/zero-work cases for `engine_imbalance`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e9e9edf. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Signed-off-by: Codex <codex@users.noreply.github.com> Signed-off-by: SumanthRH <sumanthrh99@gmail.com> Co-authored-by: Codex <codex@users.noreply.github.com>
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 05:41
e700214 to
b2ecc84
Compare
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 05:52
b2ecc84 to
de639cf
Compare
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 06:22
de639cf to
703cecc
Compare
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 07:33
703cecc to
481238d
Compare
SumanthRH
added a commit
that referenced
this pull request
Oct 4, 2026
# What does this PR do?
Publish weighted vLLM run aggregates to W&B Summary before tracking
closes.
TLDR: Reduce raw observed counters, histogram statistics and measured
durations once at the end of a fresh run.
## Why this is needed
Last-step values and averages of step rates do not describe a whole run.
For example, 1,000 tokens in 10 seconds followed by 9,000 tokens in 30
seconds gives 250 tokens/s overall, while averaging the two rates gives
200 tokens/s.
## How it works
Accumulate counter and histogram deltas over non-overlapping windows.
Compute throughput from total tokens or bytes divided by total measured
seconds, cache hit rates from total hits/queries, and latency means/P90
from merged histogram statistics. Include token, preemption and
offload-byte totals plus `measurement_seconds`.
Ray omits counters until their first nonzero increment. After validating
a complete scrape and the expected worker set, a live engine gauge
establishes zero baselines for prompt and generated token totals. This
includes the first generation in step throughput and run aggregates;
missing scrapes, reset handling and the aggregate intersection rule
retain their existing behavior.
Sync train/eval windows remain separate. Fully async collection takes a
baseline before generation and uses elapsed time between samples,
including idle and weight-sync time. Publish summaries under
`vllm_correct_aggregate/{train,eval,combined}/*`; keep `vllm/*` in step
history. Gauges remain step snapshots.
`finalize_metrics()` makes one final collection attempt with a 10-second
outer bound and closes HTTP clients. Finalization runs on success and
handled failure. Tracking publishes `run_status` and uses W&B exit code
1 for failures. Loading a checkpoint suppresses aggregates; `LATEST`
without a checkpoint remains fresh. Generic PD summaries stay disabled
in this layer; #2401 adds separate role scopes.
## Collection limits
Missing/reset windows omit the affected scope; counters missing from
part of a scope are omitted. Reused external servers require an observed
baseline. Collection does not wait for another Ray export, so short runs
or delayed terminal exports can leave aggregates unavailable or
incomplete. No metric checkpoint history or local summary files are
added.
SkyRL requests `summary="none"` and removes SDK summary entries for step
keys, but W&B can still infer last values. Use
`vllm_correct_aggregate/*` for comparisons. A hard kill cannot publish
final status or summaries.
## Validation
Tests cover weighted rates/ratios, request-weighted latency, merged
histograms, missing/reset windows, reused baselines, checkpoint
detection and finalization on success/failure/cancellation.
The cold-start regression covers sync and async collection through the
HTTP parser: the baseline contains only a live engine gauge, token
counters appear after generation, and both first-window rates and
weighted run totals include those tokens. **2 cases failed before the
fix** with missing first-window throughput keys. After the fix, **54
focused tests passed** on this PR at `f9ca0a21`, and **72 focused tests
passed** on the complete stack at `481238da` (#2401), including both
regression cases. Ruff, Black and secret checks passed.
These CPU tests use mocked HTTP responses and do not attach to the live
Ray cluster. `--noconftest` skips the repository's automatic Ray
lifecycle fixture; Ray 2.58.0 matches the validation cluster.
`--offline` uses cached uv dependencies.
```bash
uv run --isolated --offline --frozen --extra skyrl-train --extra dev --with ray==2.58.0 pytest --noconftest \
tests/train/test_grafana_annotations.py tests/train/test_metrics_lifecycle.py \
tests/train/test_vllm_run_statistics.py tests/train/test_vllm_metrics_scraper.py \
tests/train/test_vllm_window_statistics.py tests/train/test_vllm_engine_imbalance.py \
tests/train/test_vllm_pd_metrics.py tests/train/test_tracking.py -q
```
A read-only W&B/Prometheus audit of
[metrics-offload-0930-freshdash-191711](https://wandb.ai/anyscale-llm-forge/skyrl-metrics-validation/runs/9ht9q4o3)
matched **15 aggregates** within floating-point rounding: **24,576
output tokens, 73 preemptions and 259.904735 output tok/s**.
The audit used workload bounds **2026-09-30
19:21:51.471927–19:23:26.153083 UTC**, the two owned WorkerIds and
ClusterId, and terminal visibility through end +35 seconds. It checked
counter resets, reduced raw totals/histograms, and used the recorded
94.557723 seconds of generation for throughput. Workload-wall throughput
was 259.565906 tok/s, a 0.1304% difference.
That historical run used the older namespace and an export wait. It
verifies its bookkeeping, not completeness of this PR's one-attempt
terminal collection. No new GPU benchmark was run for this layer.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches training teardown, W&B lifecycle, and metrics on failure
paths; incorrect finalization could drop summaries or double-finish
runs, but behavior is guarded and heavily tested.
>
> **Overview**
> Adds **end-of-run vLLM aggregates** under
`vllm_correct_aggregate/{train,eval,combined}/*` by accumulating
counter/histogram deltas across non-overlapping windows
(`RunStatistics`), then publishing weighted throughput, cache rates,
latency, and totals via W&B **summary** (not step history). Step metrics
stay on `vllm/*`; W&B auto-summaries for those keys are disabled and
stripped on finish so dashboards should use the aggregate namespace.
>
> **`finalize_metrics()`** runs once on success or handled failure
(10s-bound final scrape, HTTP client close). Fresh runs publish scraper
summaries; **checkpoint resume** and **PD** skip aggregate summary
upload. The PPO entrypoint keeps the tracker on the experiment,
finalizes trainer metrics before exception logging, and always
**`finish()`**es tracking with **`run_status`** and a non-zero exit code
on failure.
>
> The scraper now records run windows on `sample()`/`stop()`, adds KV
offload store bytes, requires a full worker set when filtered, and
treats incomplete scrapes as omitting scope totals. Fully-async training
samples a baseline before the loop and uses the shared finalization
path.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f9ca0a2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Signed-off-by: SumanthRH <sumanthrh99@gmail.com>
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 18:46
481238d to
f6ef039
Compare
Signed-off-by: SumanthRH <sumanthrh99@gmail.com>
SumanthRH
force-pushed
the
metrics/pd-run-summaries
branch
from
October 4, 2026 18:50
f6ef039 to
0ee7e11
Compare
Signed-off-by: SumanthRH <sumanthrh99@gmail.com>
Signed-off-by: SumanthRH <sumanthrh99@gmail.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Scope vLLM step metrics and fresh-run summaries separately for prefill and decode, including externally deployed SkyRL servers in the same Ray cluster.
TLDR: Resolve the servers' frontend WorkerIds and roles, then reuse the existing worker-filtered collectors and reductions for each role.
Why this is needed
Prefill and decode can count the same prompt, prefill can report a placeholder output token, and each role measures different latency boundaries. Combining their counters or histograms obscures queue pressure and produces ambiguous run comparisons. External clients also lack the server actor handles used for managed worker discovery.
Usage
Enable
generator.inference_engine.enable_ray_prometheus_stats=trueon both the serving and client configurations. External clients specify individualexternal_server_urlswithrun_engines_locally=false, optionally together withexternal_proxy_url. The same Ray cluster must expose the servers' metrics agents. PD roles come from the servers; the client does not need to repeat their engine settings.vllm/{train,eval}/{prefill,decode}/*vllm/{prefill,decode}/*vllm_correct_aggregate/{train,eval,combined}/{prefill,decode}/*How it works
Managed deployments resolve WorkerIds from their existing server groups. External clients query
/get_metrics_worker_infoon each backend for the exporting frontend's WorkerId and optional prefill/decode role. This endpoint is exposed only with Ray stats enabled and avoids vLLM's/metricsmount. Each role gets its own collector for samples, generation windows and finalization; sync role clocks begin after both baseline reads complete.Keep each available metric in its role scope, including engine imbalance within that role. Gauges remain step snapshots. Fresh-run-only publication, missing/reset omission and the bounded terminal attempt remain; an unavailable role omits only that role's summary. Each new client takes its own baseline against a reused deployment, excluding earlier served work. Concurrent clients on the same measured engines are not distinguishable by engine counters.
Legacy external endpoints retain cluster-wide collection. Unknown-role external PD suppresses summaries when the client sets
enable_pd=true. The metrics guide documents setup and engine-level token/latency interpretation.The accompanying small simulation fix supplies the timing and checkpoint-finalization hooks now called unconditionally by the async trainer, allowing the simulation entrypoint to complete its run.
Test plan
PD cases cover sync/async role separation, weighted counters/histograms, reused baselines, gauges, missing role workers and managed setup. External setup cases cover regular serving, automatic PD role discovery, explicit PD and legacy fallback. A native vLLM HTTP-app test verifies worker metadata is not captured by the Prometheus route. Existing lifecycle cases cover unknown roles and checkpoint resumption.
Validation
180 focused tests passed, including metrics, HTTP inference, async training and collation. Ruff/Black, secret checks and the production docs build passed.
--noconftestavoids the repository's automatic Ray lifecycle fixture touching the live cluster.Real-inference validation used Qwen2.5-1.5B-Instruct/GSM8K, B200 GPUs, vLLM 0.30 and Ray 2.58, with graph execution enabled and NixlConnector for 1P1D. Two external simulation clients each completed two steps against a reused regular server; another two clients did the same against reused 1P1D servers. No gradient computation or model transfer is performed by the simulation.
Query the recorded baseline/terminal timestamps with each role's exact WorkerIds and ClusterId. Subtract observed counters, check resets, divide summed hits by summed queries, and reduce histogram sum/count deltas and merged bucket deltas. Throughput uses independently checked async observation seconds. Each client excludes earlier server work: regular/decode totals are 1,536 output tokens and 2,526 prompt tokens; prefill's 24 placeholder outputs stay separate.
W&B readback verifies all 120 PD step values and 42 regular step values. Regular client 1's second history row remains unavailable through the API, although its explicit run aggregates all match Prometheus. Earlier managed-PD evaluation validation also matched all 26 aggregates and 60 role-scoped chart values in this run.
Validation-only export waits establish visibility; PD async durations include a 25-second terminal idle wait. Production finalization has no export wait. These are compatibility checks, not a throughput benchmark.
Configs, raw observations, timestamps, queries, comparison tables and logs are retained under
/home/ray/hackskyrl/logs/1004/metrics_external_mode/and the earlier/home/ray/hackskyrl/logs/1004/metrics_pd/.Existing cleanup issue observed during validation
Both PD clients completed successfully and finalized metrics. The serving driver then raised
ActorDiedErrorduring teardown:InferenceOnlyEntrypoint._teardown()lists its PD groups twice because_server_groupsalready contains both roles. This code is unchanged in this PR and present inmain. All test servers were stopped and GPU memory release was verified. Existing aiohttp session warnings also appear after the synchronous harness closes training event loops.