Conversation
Port of 4deeb00: the reporter validates population.shape[0] == 1, so feed it the generation's best candidate. No-op at population=1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cher Clone of debug.py exposing the weekend grid axes (optimize_params, population, outer/train iters, seeds, run label via world_name). Defaults replicate the baseline exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PolicyActor.reset() rebuilt the Algorithm without stopping the previous one: each leaked APPO instance keeps ~4 background threads alive, GIL contention grows every generation, and train-iteration time climbs linearly (measured 0.24 s -> 6.5 s over 121 generations; 537 threads in the actor vs ~44 baseline). stop() kills the old threads; rebuild semantics (fresh optimizer state, set_weights to init) are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two stacked RLlib issues (Ray 2.53) with num_learners=0 (local learner): 1. Algorithm.stop() -> LearnerGroup.shutdown() only terminates REMOTE learner backends, so the local ImpalaLearner's _LearnerThread survives. 2. Setting thread.stopped alone is not enough either: the batch-wait loops (CircularBuffer.sample() and the deque path in _LearnerThread.step()) spin on an empty buffer at 10 kHz without re-checking `stopped`, so a stopped thread never leaves the wait (and burns GIL, +1 zombie/generation: measured 537 threads / 527% CPU / train-iter 0.24 s -> 6.5 s at gen 121). Fix: PolicyActor._stop_algo() flags thread.stopped, feeds one dummy entry to unblock the wait (step() re-checks `stopped` right after the dequeue), joins the thread, then calls algo.stop(). Used by both reset() and stop(). Verified: in-actor thread census flat at 2 threads over 10 generations (was +1 _LearnerThread/gen), CartPole micro-repro flat at 8 threads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This reverts commit 77e30a8. The guard fed only the generation's best candidate to plot_es_population, because the reporter asserted population.shape[0] == 1. Nadine's rework of core/reporting/utils/es_population.py (e19593e) now supports arbitrary population sizes -- it validates shape [population_size, dimension] and iterates over every candidate. Keeping the guard would silently discard the population diversity her new plots are built to show. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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 this is
Infrastructure + tooling extracted from the weekend experiment campaign, on top of
feat/fresh-water-rework. No method change: the ES, the mechanism, the smoothing, the fitness anddebug.pyare all untouched. Merges cleanly into the current tip (e19593e).Contents
f117cfcruntime.py,_worker_hooks.py)local_mode(hardcodedTruebreaks on Ray 2.53 when launched from inside the editable repo), BLAS/torch thread caps propagated to workers (uncapped → 1083 threads / 16 cores → whole-machine freeze), per-tasksetproctitlesilenced (sync XPC round-trip tolaunchservicesdon every task → UI freeze at 90% idle CPU), and no more repo/.venvupload to workers.3114cfd+b46ccf9_LearnerThreadleak fix (policy_actor.py)e634f72exp_run.py, CLI-parameterized clone ofdebug.py--population/--optimize-params/--outer-iters/--seedwithout editingdebug.pybetween runs. Defaults replicate your config exactly.7a63803plot_es_populationpop>1 guardes_population.pyrework (e19593e) supports arbitrary population sizes properly, so our workaround would now discard the diversity your new plots show.The
_LearnerThreadleak (likely cause of the crashed/slow runs)Two stacked RLlib issues in Ray 2.53, both only when
num_learners=0(local learner):PolicyActor.reset()rebuilds the Algorithm every generation without stopping the previous one → its background threads stay alive.algo.stop(), one thread survives per generation:Algorithm.stop()→LearnerGroup.shutdown()only terminates remote learner backends. The localImpalaLearner's_LearnerThreadkeeps running, and it is stuck insideCircularBuffer.sample()whose wait loop (while len(self)==0: time.sleep(0.0001),appo/utils.py:104) spins at 10 kHz without re-checkingstopped.Each zombie thread burns GIL at 10k wakeups/s. Measured on a 1000-generation run: train-iteration time 0.24 s → 6.5 s over 121 generations (linear growth → quadratic total time, unreachable end), 537 threads in the actor vs ~44 baseline, 527% CPU of pure lock contention.
Fix (
_stop_algo()): flagthread.stopped, push one dummy entry intothread._in_queueto unblock the wait (step()re-checksstoppedimmediately after the dequeue),join(5 s), thenalgo.stop(). Used by bothreset()andstop().Verified: in-actor thread census flat at 2 over 10 generations (was +1/gen); full run flat at 12.0–12.7 s/gen through gen 60 (was 17 → 37 s/gen and 96 threads at the same point); 4 consecutive multi-hour runs completed with
exit=0.Weekend results (all 6 runs on your wandb, project
bilevel)Best objective, grid {population} × {mechanism dimensionality}:
min_demand_frac)+fixed_quota)num_envs_per_env_runner=16; your advantage-baseline branch switches to fitness whitening automatically at N>1.0.2500, reached by ~generation 50 and never exceeded. That looks like a structural cap of the objective (fitness normalization / sustainability weighting) rather than an optimum — worth a look.Notes
.gitignoregains.wandb_nadine.env(local API-key file, never committed).🤖 Generated with Claude Code