Repository navigation
feat(resources): heterogeneous job components - #5
Merged
Merged
Conversation
Some work needs two machine shapes at once — e.g. a CPU node serving environment VMs alongside the GPU node that trains against them. Splitting that into two labctl runs loses exactly what makes the pairing work: co-scheduling (the GPUs idle while the CPU side queues, which on a busy cluster is tens of GPU-hours) and coupled teardown (a crashed trainer leaves the fleet holding a node). Add `[[resources.components]]`: each entry emits a `#SBATCH hetjob` separator and its own directive block, so one recipe allocates one SLURM heterogeneous job. Job-wide settings (time, qos, account, dependencies, log paths) stay on component 0 per SLURM semantics. Defaults to empty, so existing recipes render byte-identically. Run state needs no changes: `sacct -j <id>` reports one row per component, which scheduler_outcome already aggregates the same way it aggregates array elements, and `scancel <id>` cancels every component. Tests assert the emitted directives (separator count, per-component gres/cpus/mem/nodes, no duplicated job-wide flags, and that a recipe without components stays het-free). Verified end-to-end on this cluster: a job rendered with this syntax allocated two components, reported by sacct as <id>+0 and <id>+1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
avocadoali
approved these changes
Aug 17, 2026
Contributor
|
lgtm |
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.
Why
Some work needs two machine shapes at once. The concrete case: a CPU node serving environment VMs alongside the GPU node that trains against them (
p-doom/reinforcement-learning's OSWorld/CUA-Gym runs do exactly this, via a hand-written het sbatch).Today a recipe can only describe one shape, so bringing those runs under labctl means splitting them into two runs — which costs the two properties that make the pairing work:
Plus you'd need an out-of-band rendezvous, since a long-lived service doesn't fit
[inputs.*] type = "artifact"(artifacts model completed things).What
[[resources.components]]. Each entry emits a#SBATCH hetjobseparator and its own directive block, so one recipe allocates one SLURM heterogeneous job:Job-wide settings (
time,qos,account, dependencies, log paths) stay on component 0, per SLURM semantics. The field defaults to empty, so existing recipes render byte-identically — there's a test for that.SLURM runs the batch script body on component 0 only, so
commandplaces work on the others withsrun --het-group=N;examples/recipes/hetjob.tomlshows the pattern and the docs call it out explicitly.What didn't need changing
Run state.
sacct -j <id>reports one row per component, andscheduler_outcomealready aggregates multi-row output for array jobs with exactly the right priority rule (any failure trumps; running outranks succeeded).scancel <id>cancels every component.Validation
cargo test— 32 + 9 pass,cargo fmt --checkclean.--gres/--cpus-per-task/--mem/--nodes, that job-wide--timeisn't duplicated, that component 0's own resources survive, that a component'ssbatch_extrarejects a#SBATCHprefix like the top-level one does, and that a recipe without components stays het-free.sacctreported140467+0 COMPLETEDand140467+1 COMPLETED.Note, not included
.gitignorecontains only*.log— notarget/. Agit add -Ain a built checkout stages ~5k files including a 123 MB binary (it caught me). Left out to keep this single-concern, but worth a one-liner.🤖 Generated with Claude Code