LAB 4 / RESULTS

Results and write-up

The sweep across world size, the figures it produces, and what the write-up has to argue. These are measured numbers from the reference implementation, not predictions.

Resident bytes per parameter, measured

Sweep across world size — measured, and matching the formulas exactly
NDPZeRO-1ZeRO-2ZeRO-3
116.000016.000016.000016.0000
216.000010.00009.00008.0000
416.00007.00005.50004.0000
816.00005.50003.75002.0000
1616.00004.75002.87501.0000
3216.00004.37502.43750.5000

Three things to point at in the write-up. The DP column never moves — adding hardware does nothing for it. ZeRO-1 and ZeRO-2 flatten toward 4 and 2, their replicated floors, and will never go below them. ZeRO-3 keeps halving, because it replicates nothing.

At N = 1 all four are identical, which is worth including rather than skipping: with one rank there is nothing to shard, and a correct implementation should say so.

Peak, which is the number that decides fit

Peak bytes per parameter, including the gathered copy
NDPZeRO-1ZeRO-2ZeRO-3 residentZeRO-3 peak
416.0007.0005.5004.0008.000
816.0005.5003.7502.0006.000
1616.0004.7502.8751.0005.000
3216.0004.3752.4380.5004.500

The ZeRO-3 peak falls much more slowly than its resident figure, and at N = 32 it is worse than ZeRO-2's resident 2.44. That is an artefact of the toy model gathering all 33,088 parameters at once, and it is worth saying so plainly: a real ZeRO-3 gathers one layer, so the added term is the largest layer rather than the whole model. The prototype overstates this cost, and pretending otherwise would be the easiest way to draw a wrong conclusion from correct code.

An improvement worth making if there is time

Gather per layer rather than per model. The MLP already stores per-layer slices, so ZeRO-3 can loop over them, gather one, use it, release it. Peak then becomes 16/N + 4 × (largest layer / n), which is the real behaviour, and the peak column stops being misleading.

The figures to produce

Four plots, in the order they should appear
FigureShowsWhy it is there
loss curves, all four overlaidone visible linethe implementation is ZeRO, not an approximation of it
max parameter difference vs stepa flat line at zerothe claim above, as a number
bytes per parameter vs N, log-logtwo curves flattening, one descendingreplicated state sets a floor
traffic per step vs Nthree curves identical, one 1.5× higherstages 1 and 2 are free; stage 3 is not
the sweep that produces them
import numpy as np
from zerosim.model import MLP
from zerosim.schemes import SCHEMES

m, STEPS = MLP(), 3
rows = {}
for N in (1, 2, 4, 8, 16, 32):
    rng = np.random.default_rng(3)
    batches = [[(rng.standard_normal((4, 64)).astype(np.float32),
                 rng.standard_normal((4, 64)).astype(np.float32))
                for _ in range(N)] for _ in range(STEPS)]
    out = []
    for S in SCHEMES:
        s = S(m, N)
        for t in range(STEPS):
            s.step(batches[t])
        out.append((s.bytes_per_param(),
                    s.peak_bytes_per_param(),
                    s.bytes_comm_per_rank() / STEPS / (m.n_params * 4)))
    rows[N] = out

The write-up

The numbers and the structure come from the experiment. The reasoning is the part that has to be yours, and the most useful thing to write about is the places where the measurements do not match the tidy story.

Six questions the README should answer, with the measurement that supports each
QuestionWhat to point at
What does each stage shard?the three flags, and which of the sixteen bytes each one covers
Why are stages 1 and 2 free?the traffic table: three identical columns
Why does stage 3 cost more?two gathers instead of one, because the gathered copy is released between passes
Why can ZeRO-1 never fit some models?the sweep flattening at 4 bytes, and what that is as GiB for a real model
Which would you choose?your own peak-memory and traffic numbers, and which constraint bound first
Where does ZeRO stop?the peak column — when one gathered layer exceeds a card, state sharding is done

Three things worth admitting in the README

Traffic never reaches 2P. It is 2(N−1)/N, so 1.9375P at N = 32. The quoted 2P is an upper bound that gets truer as N grows.

The peak figure is pessimistic. Gathering the whole model rather than one layer inflates ZeRO-3's peak, for the reason given above.

Bitwise equality is a property of this implementation, not of ZeRO. A real framework reduces in a different order and will differ in the last few bits.

Each of these is a place where the honest answer is more informative than the clean one, and noticing them is the difference between running the code and understanding it.

What is still missing: activation memory, which this prototype does not model at all. Every figure here is the sixteen bytes per parameter. On a real run the activations share the same card, and past a certain sequence length they are the larger pile — which is the one thing the prototype cannot tell you.
BACKThe two checks