feat(pipeline): submit parallel steps in waves of array jobs - #350
Merged
Conversation
When a parallel step's open-job count exceeds max_array_size * max_jobs_per_task, the orchestrator now submits it as multiple sequential waves of array jobs instead of one strided array, so the declared SlurmResources always cover exactly max_jobs_per_task experiments (default 1) per array task, independent of how many open jobs the step has. - SlurmResources.max_jobs_per_task: int | None = 1 (None restores the single strided submission and renders byte-identical scripts, guarded by golden files captured from the pre-wave renderer) - Wave assignment reads the frozen central-store snapshot: open[wave*W*j:][task::W][:j], generalizing the previous stride - Waves chain afterany internally; the next step's dependency attaches to the final wave (see docs/adr/0002) - Orchestrator gains a fourth WAVE_COUNT counter; the wave index reaches workers via exported F3DASM_WAVE -> run_step --wave - New bash harness executes rendered orchestrators against a fake sbatch and asserts the full submission trace Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Path.read_text() defaults to the platform codec (cp1252 on Windows), which mangles the em-dash in the golden orchestrator script and fails the byte-identity assertion on the windows-latest CI runners. Co-Authored-By: Claude Fable 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.
Summary
When a parallel step's open-job count exceeds
max_array_size * max_jobs_per_task, the orchestrator now submits it as multiple sequential waves of array jobs instead of one strided array. The declaredSlurmResourcestherefore always cover exactlymax_jobs_per_taskexperiments (default 1) per array task, independent of how many open jobs the step has — previously,time/memsilently had to coverceil(n_open / max_array_size)sequential experiments, a number not even knowable when the config is written.Design rationale, rejected alternatives (consolidate-and-recount between waves,
afterokwave chaining, opt-in default), and the frozen-snapshot invariant are recorded indocs/adr/0002-wave-based-array-submission.md; the Wave term is defined inCONTEXT.md.Behavior
SlurmResources.max_jobs_per_task: int | None = 1— new field, validated in__post_init__.Noneopts out and restores the single strided submission, rendering byte-identical scripts to the previous renderer (guarded by golden files captured fromdevelopbefore this change).kof wavebownsopen[b*W*j:][k::W][:j]— the direct generalization of the previous stride expression (b=0, j=Nonereproduces it exactly). Per-sample result JSONs are deliberately not consulted between waves: race-free, deterministic wave count, no mid-step mutation of the central store.afterany(an infra-killed task must not orphan the remaining waves); the next pipeline step'sdependencyattaches to the final wave only.WAVE_COUNT; the wave index reaches workers via exportedF3DASM_WAVE(theF3DASM_ITERATIONmechanism) →run_step --wave→ExecutionContext.wave. All wave plumbing is rendered only when some step actually uses waves.n_open <= max_array_size(the common case): each task already evaluated exactly one job there.Tests
test_orchestrator_exec.py): runs rendered orchestrators underbashwith a fakesbatch+ stubbedcount_open, feeding eachsbatch "$SELF" …resubmission back in as the next wake. Asserts the full submission trace: per-wave array widths (incl. ragged last wave),afteranychaining, final-waveafterokadvance,F3DASM_WAVEvalues, wave-counter resets across loop iterations. Skipped on Windows.test_golden_scripts.py+golden/):max_jobs_per_task=Nonereproduces the pre-wave renderer byte-for-byte.ExecutionContext.assigned_jobs(waves × tasks partition the open jobs: disjoint, complete, each ≤j),--waveCLI threading,SlurmResourcesvalidation, and string assertions for the new render paths.pytest(1125 passed, coverage 91.8%),ruff check,pre-commit run --all-files, andmkdocs buildall pass locally.Release
Version bumped to 2.3.0 (new public field + deliberate behavioral change in the overflow regime). Rollback for users who want the old strided behavior:
max_jobs_per_task=None.🤖 Generated with Claude Code