Skip to main content
A job asks for cards with gpu_count. What you do with them is one of two shapes, and both are one line.

Many trainings, one card each

Four cells, four cards, one call; the wall clock is one cell’s. Nothing here talks to NCCL, which is why this is the shape to reach for first: on a cluster of RTX PRO 6000s a four-cell sweep finished in 92 s against 73 to 78 s for any single cell.

One training, many cards

distributed.run starts one process per card the container was given, gives each its rank, joins them into a process group, calls your function on every rank and returns what rank 0 returned. Change gpu_count to 2 or 8 and nothing else changes. With gpu_count=1 there is nothing to spawn: the function runs in place and every helper answers as a world of one, so one function covers 1x, 2x, 4x and 8x. Inside the function, the helpers are the handful a loop needs:
dist.shard(rows) gives a rank its slice of a list when a static split is simpler than indexing. The function must be a module-level function (ranks are separate processes and import it by name), and everything it needs goes in through its arguments.
A step trains world_size examples instead of one, so an effective batch of 32 on four cards is 8 per card; adjust gradient accumulation accordingly if a run is meant to be comparable to a single-card one.

torchrun and accelerate

Under torchrun or accelerate launch the process is already a rank. distributed.run notices, joins the group and calls the function; the helpers read the same RANK / WORLD_SIZE / LOCAL_RANK those launchers set. Code written for them keeps working.

What the cards need

Cards have quirks, and they live in levers9.distributed rather than in every training script. Anything you set yourself wins over these defaults.

Sizing, measured

On four RTX PRO 6000s in one node, the same 10,170-step fine-tune: 3.15x on four cards is what NCCL through shared memory costs. Four single-card cells, when the work is independent, get the full 4x, which is why the sweep shape comes first above.

What a multi-card job waits for

A multi-card job needs one worker holding that many cards, so it waits for a node with that many free at the same moment. On an attached cluster the agent reports what each node has free; a node whose other cards belong to another deployment offers only what is left. To borrow a whole node, scale the neighbour down and the job starts as soon as the cards show as free. Two limits gate more than one card per job: the controlplane’s per-stub cap (stubLimits.maxGpuCount, raised to 8 on the hosted deployment) and the workspace’s own switch:
It is a workspace setting because several cards in one container is a whole node held by one call. It takes effect on the next call: the change clears the cached copy of the workspace that tokens carry.