fix(sc): set step_finished=True in async GRPO logging - #3816
Merged
Merged
Conversation
Contributor
Author
|
/ok to test 92d0ab0 |
RayenTian
marked this pull request as ready for review
August 25, 2026 06:13
RayenTian
force-pushed
the
ruit/v2_2766_log_switch
branch
from
August 25, 2026 08:49
92d0ab0 to
f5d0611
Compare
Contributor
Author
|
/ok to test f5d0611 |
Contributor
Author
|
/ok to test fb4529d |
yuki-97
reviewed
Aug 25, 2026
yuki-97
left a comment
Contributor
There was a problem hiding this comment.
One note on the SC port: the concurrent stall watchdog logs to the same step, which v1's async loop does not have.
Mirror #2766 for the v2 single-controller entrypoint: mark the final timing/train log of each step as step_finished so W&B commits the step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: ruit <ruit@nvidia.com>
The train_pump now passes step_finished=True to the final timing/train log; align the test's fake logger signature with the real Logger so it does not raise TypeError. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: ruit <ruit@nvidia.com>
Record step_finished in the fake logger and assert the final timing/train log carries step_finished=True while the train log does not, so the fix is guarded against regression instead of merely tolerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: ruit <ruit@nvidia.com>
_train_pump now commits its step, and _train_steps is not incremented again until the next step finishes, so every stall-watchdog tick in between named a step wandb had already closed and was dropped with a warning. Route the tick through step_metric, which takes the logger's commit=False branch and sends no step, so it accumulates into the open step instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: ruit <ruit@nvidia.com>
Record step_metric in the fake logger and assert every watchdog tick passes "rollout/train_steps" and carries that key in its payload. WandbLogger takes the no-step branch only when both hold, so dropping the key alone would silently restore the dropped-tick bug. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: ruit <ruit@nvidia.com>
RayenTian
force-pushed
the
ruit/v2_2766_log_switch
branch
from
August 27, 2026 06:03
fb4529d to
5657f01
Compare
Contributor
Author
|
/ok to test 5657f01 |
Collaborator
|
/ok to test 5657f01 |
yuki-97
approved these changes
Aug 27, 2026
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.
Streams #2766 to the v2 single-controller entrypoint.
#2766 added
step_finished=Trueto the finaltiming/trainlog_metricscall in the v1 async GRPO loop (grpo.py:async_grpo_train), so W&B commits the step (commit=True) instead of buffering the step's last metrics until the next step.The v2 entrypoint
examples/run_grpo_single_controller.pydrivesSingleControllerActor(nemo_rl/algorithms/single_controller.py) instead ofgrpo.py, so #2766 didn't cover it. Its async training loop_train_pumphas the same structure, wheretiming/trainis the final per-step log. This applies the identical fix there.Follow-up: the stall watchdog
Thanks to @yuki-97 for catching this — the port is not equivalent as-is, because v2 has a concurrent writer to the same step that v1's
async_grpo_traindoes not._stall_watchdog_pumppublishes rollout counters everyasync_rl.stall_watchdog.interval_s(default 30s) atstep=self._train_steps._train_stepsis incremented atsingle_controller.py:1258, before the logging block this PR touches, and is not incremented again until the next step finishes. So committing at the end of step N leaves every watchdog tick for the whole duration of step N+1 naming step N — a step W&B has already closed.W&B 0.28.1 drops a write below the current step (
handler.go:972-993) and prints...this data will be ignoredon every tick. Measured ongrpo-qwen2.5-math-1.5b-instruct-1n8g-megatron-single-controller-sync(20 steps, ~10.9s/step, 7 ticks): with thetiming/traincommit alone, 1 tick landed and 6 of 7 were dropped, one warning each. Without a second change this PR would have silently killed therollout/*metrics.Fix
Route the watchdog log through
step_metric, which:1453already populates:logger.py:384-386takes that branch and callsrun.log(metrics, commit=False)with nostep=. W&B's monotonicity check is guarded on the step field being present, so it never runs, and the tick accumulates into the currently open step instead of being discarded. TensorBoard (logger.py:164-165) and MLflow ignorestep_metric, so nothing changes there.Note that
step_finished=Falsewould not work here: it is already the default, and the watchdog's write is alreadycommit=False. What gets it dropped is sending a step number at all, not committing.Trade-offs, both unchanged in kind from today's behaviour:
rollout/train_stepsis logged as a value, so the tick's true step number stays recoverable from the data.Test
test_watchdog_pump.py::TestMetrics::test_ticks_never_name_the_committed_steprecordsstep_metricin the fake logger and asserts every tick passes"rollout/train_steps"and carries that key in its payload.logger.py:384needs both — dropping the key alone would silently fall back to sending a step and restore the bug with nothing failing. Verified red without the fix, green with it.W&B panels to expect
The
rollout/*group (committed_total,inflight,idle_s,redispatch_total, …) goes from "dropped after the first tick" back to fully populated.The visible addition is a
rollout/train_stepspanel — a monotonically rising step counter. It was already in the metrics dict at:1453, but promoting it to thestep_metricargument is what makes it meaningful to read on its own. This is the direct analog of the existingray/ray_steppanel thatRayGpuMonitorLoggerproduces through the same mechanism (logger.py:1019-1029).One difference from the GPU monitor: that path pairs its
step_metricwith adefine_metric("ray/*", step_metric="ray/ray_step")call, soray/*plots againstray/ray_step. This call site does not, sorollout/*plots against W&B's internal step androllout/train_stepsis read as an ordinary series. Worth adding adefine_metriclater if the one-step offset becomes confusing in practice.🤖 Generated with Claude Code