LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnProject Deep DivesDeep Dive - DeepSpeed
⚡HardFine-Tuning & Training

Deep Dive - DeepSpeed

Trace DeepSpeed from ZeRO ownership and dtype-aware memory accounting through gathered working sets, sharded Adam updates, offload, and checkpoint recovery.

20 min read
Learning path
Step 187 of 196 in the full curriculum
Deep Dive - MegatronDeep Dive - Ray

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

A model can fit its weights on one GPU and still run out of memory at the first optimizer update. A 1.5-billion-parameter model needs 3 GB for two-byte weights alone. In the ZeRO paper's mixed-precision Adam accounting, training state takes 24 GB before activations or temporary buffers. Where did the other 21 GB come from?[1]Reference 1ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.https://arxiv.org/abs/1910.02054

The Megatron lesson split computation across parallel axes. DeepSpeed also asks who must retain each tensor between computations. Its Zero Redundancy Optimizer (ZeRO) partitions optimizer state, gradients, and eventually parameters across data-parallel ranks. Each rank still processes its own examples; sharding state doesn't mean sharding the examples' computation as tensor parallelism would.

We'll follow the memory accounting into the engine, a gathered module, and a checkpoint. The source tour uses commit c455031422641a825588c926455458c82b27b6c0 (August 6, 2026, before the 0.19.4 release); the current documentation consulted identifies itself as 0.19.6. These are distinct version references, not evidence that every feature combination works on both.[2]Reference 2DeepSpeed Source Repositoryhttps://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0[3]Reference 3Training APIhttps://deepspeed.readthedocs.io/en/latest/training.html

The marked examples run on a CPU with Python's standard library. They check ownership arithmetic and an Adam update, not DeepSpeed kernels, distributed execution, or GPU memory measurements.

Count state before choosing a stage

Let PPP be the parameter count represented by a data-parallel group. If tensor or pipeline parallelism already splits the model, use the relevant local model partition, not the entire model's parameter count.

The paper's 16-byte recipe is:

TensorBytes per parameterRole
Low-precision parameter2Forward and backward computation
Low-precision gradient2Gradient awaiting the update
FP32 master parameter4Higher-precision update state
FP32 first moment4Adam's running gradient average
FP32 second moment4Running average of squared gradients
Total16Model state only

The second moment isn't an estimate of centered variance. Also, BF16 compute doesn't by itself determine the gradient-accumulation dtype or whether a master copy exists. For example, replacing the two-byte gradient with a four-byte accumulation array changes this simplified total to 18 bytes. Inspect the actual optimizer and buffer dtypes. DeepSpeed exposes data_types.grad_accum_dtype separately.[4]Reference 4DeepSpeed Configuration JSONhttps://www.deepspeed.ai/docs/config-json/

Let ppp, ggg, and ooo denote bytes per parameter for compute weights, gradients, and optimizer-plus-master state. For NNN data-parallel ranks, idealized per-rank storage is:

MDP=P(p+g+o),MZ1=P(p+g+o/N),MZ2=P(p+(g+o)/N),MZ3=P(p+g+o)/N.\begin{aligned} M_{DP} &= P(p+g+o),\\ M_{Z1} &= P(p+g+o/N),\\ M_{Z2} &= P(p+(g+o)/N),\\ M_{Z3} &= P(p+g+o)/N. \end{aligned}MDP​MZ1​MZ2​MZ3​​=P(p+g+o),=P(p+g+o/N),=P(p+(g+o)/N),=P(p+g+o)/N.​

With (p,g,o)=(2,2,12)(p,g,o)=(2,2,12)(p,g,o)=(2,2,12) and N=4N=4N=4, those are 16P16P16P, 7P7P7P, 5.5P5.5P5.5P, and 4P4P4P bytes. The formulas describe balanced persistent state, not peak allocator usage. Padding, persistent small parameters, gathered tensors, activations, communication buffers, workspaces, and fragmentation can add memory.[1]Reference 1ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.https://arxiv.org/abs/1910.02054

Idealized model state per rank for four data ranks. Replicated data parallel: parameters two, gradients two, optimizer plus master twelve, total sixteen bytes per parameter. ZeRO-1 totals seven, ZeRO-2 five point five, and ZeRO-3 four. Every row uses the same scale.
ZeRO removes replication in stages. The large first reduction comes from sharding the twelve-byte optimizer-plus-master block; the remaining reductions change gradient and parameter ownership.

Use exact fractions to compare recipes without confusing GB (10910^9109 bytes) and GiB (2302^{30}230 bytes). This estimator deliberately excludes padding and live working sets.

zero_memory.py
1from fractions import Fraction 2 3def state_bytes(parameters, ranks, param=2, grad=2, optimizer=12): 4 values = (parameters, ranks, param, grad, optimizer) 5 if any(type(x) is not int or x <= 0 for x in values): 6 raise ValueError("counts and byte widths must be positive integers") 7 shard = Fraction(parameters, ranks) 8 return { 9 "DP": parameters * (param + grad + optimizer), 10 "ZeRO-1": parameters * (param + grad) + shard * optimizer, 11 "ZeRO-2": parameters * param + shard * (grad + optimizer), 12 "ZeRO-3": shard * (param + grad + optimizer), 13 } 14 15for name, size in state_bytes(1_500_000_000, 4).items(): 16 print(f"{name:6s} {float(size / 10**9):5.2f} GB | {float(size / 2**30):5.2f} GiB") 17assert set(state_bytes(17, 1).values()) == {17 * 16} 18assert state_bytes(1_000_000_000, 4, grad=4)["ZeRO-3"] == 4_500_000_000
Output
1DP 24.00 GB | 22.35 GiB 2ZeRO-1 10.50 GB | 9.78 GiB 3ZeRO-2 8.25 GB | 7.68 GiB 4ZeRO-3 6.00 GB | 5.59 GiB

Stage 1 partitions optimizer state and master weights; stage 2 also partitions reduced gradients; stage 3 also partitions compute parameters.[5]Reference 5ZeRO Configurationhttps://deepspeed.readthedocs.io/en/stable/zero3.html Higher stages aren't quality levels. If stage 2 fits, stage 3's additional gathers may or may not pay off. If saved activations dominate, investigate activation checkpointing, microbatch size, or sequence length before expecting parameter sharding to solve the problem.

Reduce first, then update the owned slice

ZeRO's central arithmetic is easiest to see with two ranks. Suppose their gradients for one parameter are 1 and 3. The averaged gradient is 2. The owner applies Adam once to that gradient and its local moment state. It mustn't apply Adam separately to each rank's gradient and then average the updated moments: the second moment involves a square, and (12+32)/2=5(1^2+3^2)/2=5(12+32)/2=5, while ((1+3)/2)2=4((1+3)/2)^2=4((1+3)/2)2=4.[6]Reference 6Adam: A Method for Stochastic Optimization.https://arxiv.org/abs/1412.6980

In an unsharded run, every rank can hold the reduced gradient and optimizer state. In a sharded run, the owning rank holds the relevant slice. The mathematical update can match, although different reduction orders and low-precision arithmetic can change floating-point results.

This scalar implementation runs five updates on eleven parameters, partitioned over four logical owners. The comparison covers every weight and moment, including uneven partitions. The reference uses ordinary Adam without weight decay, not the later AdamW configuration, and doesn't simulate NCCL's execution or DeepSpeed's padding layout.

owned_adam.py
1from math import isclose, sqrt 2 3def adam_step(weight, first, second, gradient, step, lr=0.01): 4 first = 0.9 * first + 0.1 * gradient 5 second = 0.999 * second + 0.001 * gradient * gradient 6 corrected_first = first / (1 - 0.9**step) 7 corrected_second = second / (1 - 0.999**step) 8 weight -= lr * corrected_first / (sqrt(corrected_second) + 1e-8) 9 return weight, first, second 10 11def compare_owned_updates(parameters, ranks, steps): 12 if any(type(x) is not int or x <= 0 for x in (parameters, ranks, steps)): 13 raise ValueError("parameters, ranks, and steps must be positive integers") 14 reference = [(0.1 * i, 0.0, 0.0) for i in range(parameters)] 15 # Contiguous ownership; some owners may be empty when ranks > parameters. 16 owners = [range(parameters * r // ranks, parameters * (r + 1) // ranks) 17 for r in range(ranks)] 18 sharded = [{i: reference[i] for i in ids} for ids in owners] 19 assert sorted(i for owner in sharded for i in owner) == list(range(parameters)) 20 for step in range(1, steps + 1): 21 local = [[((i + 2 * r + step) % 7 - 3) / 10 22 for i in range(parameters)] for r in range(ranks)] 23 mean = [sum(local[r][i] for r in range(ranks)) / ranks 24 for i in range(parameters)] 25 reference = [adam_step(*state, mean[i], step) 26 for i, state in enumerate(reference)] 27 for rank, owner in enumerate(sharded): 28 # A reference reduce-scatter: reduce across ranks, retain owned indices. 29 reduced = {i: sum(row[i] for row in local) / ranks for i in owner} 30 sharded[rank] = {i: adam_step(*state, reduced[i], step) 31 for i, state in owner.items()} 32 reconstructed = [state for owner in sharded for state in owner.values()] 33 assert all(isclose(a, b, rel_tol=1e-12, abs_tol=1e-12) 34 for expected, actual in zip(reference, reconstructed) 35 for a, b in zip(expected, actual)) 36 return reference 37 38result = compare_owned_updates(11, 4, 5) 39# Independently check the first-step scalar update for gradient 2. 40w, m, v = adam_step(1.0, 0.0, 0.0, 2.0, 1) 41assert isclose(w, 1.0 - 0.01 * 2.0 / (2.0 + 1e-8)) 42assert isclose(m, 0.2) and isclose(v, 0.004) 43assert (1**2 + 3**2) / 2 != ((1 + 3) / 2)**2 44print(f"{len(result)} parameters, 4 owners, 5 updates: weights and moments match")
Output
111 parameters, 4 owners, 5 updates: weights and moments match

The example averages equally weighted rank gradients. Variable microbatch sizes or different numbers of unmasked tokens need the appropriate loss weighting; “average each rank's average” isn't automatically the global token-average objective.

Let the engine own the microstep

deepspeed.initialize(...) selects and configures a DeepSpeedEngine, wrapped optimizer, optional data loader, and scheduler. The engine coordinates backward, accumulation, reduction, precision, stepping, and checkpoint state.[2]Reference 2DeepSpeed Source Repositoryhttps://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0

Here is an integration sketch, not a standalone program. It assumes a model whose forward returns a scalar mean loss, a prepared batch on the correct device, and a distributed launcher. Under the default managed-accumulation behavior, call step() after every microbatch; the engine applies an optimizer update at its accumulation boundary. Don't divide the loss by the accumulation count again when using the default engine scaling.[3]Reference 3Training APIhttps://deepspeed.readthedocs.io/en/latest/training.html

engine_integration_sketch.py
1engine, optimizer, _, scheduler = deepspeed.initialize( 2 model=model, 3 model_parameters=model.parameters(), 4 config="ds_config.json", 5) 6for batch in prepared_batches: 7 loss = engine(batch) 8 engine.backward(loss) 9 engine.step()

This four-data-rank configuration uses BF16 model parameters and explicitly requests FP32 gradient accumulation. It's a starting configuration to validate on compatible GPUs, not a benchmarked recommendation. The optimizer settings belong to your training recipe; ZeRO doesn't determine a suitable learning rate.

ds_config.json
1{ 2 "train_batch_size": 64, 3 "train_micro_batch_size_per_gpu": 2, 4 "gradient_accumulation_steps": 8, 5 "bf16": { "enabled": true }, 6 "data_types": { "grad_accum_dtype": "fp32" }, 7 "zero_optimization": { 8 "stage": 2, 9 "contiguous_gradients": true, 10 "overlap_comm": true, 11 "reduce_bucket_size": 200000000 12 }, 13 "optimizer": { 14 "type": "AdamW", 15 "params": { 16 "lr": 0.00002, 17 "betas": [0.9, 0.999], 18 "eps": 1e-8, 19 "weight_decay": 0.01 20 } 21 } 22}

Check two different units. The batch identity is 2×8×4=642\times8\times4=642×8×4=64 samples per optimizer update. The last factor is the data-parallel degree, not necessarily the total number of GPUs when model parallelism is present. Holding all three batch fields fixed while launching eight data ranks is inconsistent, not a harmless scale-out change.[4]Reference 4DeepSpeed Configuration JSONhttps://www.deepspeed.ai/docs/config-json/

reduce_bucket_size is a count of elements, not bytes. Two hundred million elements occupy 400 MB at two bytes each or 800 MB at four bytes each, before other buffers. Communication dtype and accumulation dtype needn't match. Larger buckets can amortize collective overhead but increase temporary storage and delay the first reduction; inspect the timeline rather than copying a bucket size.[5]Reference 5ZeRO Configurationhttps://deepspeed.readthedocs.io/en/stable/zero3.html

For a real launch, record the installed DeepSpeed and PyTorch versions, accelerator and driver, the resolved configuration, and ds_report output. The report checks operator/environment compatibility; it doesn't prove that a multi-rank training step or offload path works.[7]Reference 7DeepSpeed Installation Detailshttps://www.deepspeed.ai/tutorials/advanced-install/ Neither the integration sketch nor this GPU configuration was executed for this lesson.

Stage 3 needs a live working set

A sharded parameter can't directly supply a conventional full-layer matrix multiplication. ZeRO-3 gathers the parameter view needed for a module, executes it, and can release that full view afterward. Backward can require another gather. Prefetch, persistence, reuse, and module grouping affect the actual schedule.[5]Reference 5ZeRO Configurationhttps://deepspeed.readthedocs.io/en/stable/zero3.html

Illustrative ZeRO-3 schedule without persistent full parameters: gather, forward, release; then gather again, backward, and reduce gradients to owners. Local Adam updates occur at the accumulation boundary.
Forward and backward may need separate parameter gathers. The gradient reduction gives each owner its update slice; an optimizer step waits for the accumulation boundary. Persistence can skip some releases and gathers.

Don't subtract the local shard from a gathered allocation unless the implementation actually aliases that storage. In the pinned source's sequential gather path, param.ds_tensor is the input shard and a separate torch.empty(...) tensor receives the full padded gather.[2]Reference 2DeepSpeed Source Repositoryhttps://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0 Logical values and allocated bytes are different counts.

For four tensors of eight parameters on four ranks, one rank retains eight shard elements in total. A separate full eight-element gather brings the parameter-storage count to sixteen, not fourteen. Keeping a second gathered tensor for prefetch raises this example to twenty-four. These counts exclude gradients, optimizer state, activations, and communication-library workspace.

Separate-allocation model for four eight-element tensors on four ranks. Each rank retains eight shard elements. One full gathered tensor adds eight elements for sixteen total; prefetching another adds eight more for twenty-four. The local values appear in both shard and full buffer.
Count allocations that coexist, not only values received from peers. Prefetch trades a larger live working set for a chance to hide the next gather; it doesn't guarantee that the transfer is hidden.

This storage model rounds each tensor's shard up to a whole element and allocates a separate padded gather buffer. It models one specified layout, not a universal DeepSpeed peak estimator.

gather_storage.py
1def parameter_storage(layer_sizes, ranks, live_layers): 2 if type(ranks) is not int or ranks <= 0: 3 raise ValueError("ranks must be a positive integer") 4 if not layer_sizes or any(type(n) is not int or n <= 0 for n in layer_sizes): 5 raise ValueError("tensor sizes must be positive integers") 6 if (any(type(i) is not int or not 0 <= i < len(layer_sizes) for i in live_layers) 7 or len(set(live_layers)) != len(live_layers)): 8 raise ValueError("live tensor indices must be distinct and in range") 9 partitions = [(n + ranks - 1) // ranks for n in layer_sizes] 10 resident = sum(partitions) 11 gathered = sum(partitions[i] * ranks for i in live_layers) 12 return resident, gathered, resident + gathered 13 14sizes = [8, 8, 8, 8] 15for live in ([], [0], [0, 1]): 16 resident, gathered, total = parameter_storage(sizes, 4, live) 17 print(f"{len(live)} gathered: shards {resident} + full buffers {gathered} = {total} elements") 18assert parameter_storage([5, 9, 2], 4, [1]) == (6, 12, 18) 19assert parameter_storage(sizes, 4, [0])[2] == 16 20assert parameter_storage(sizes, 4, [0, 1])[2] == 24
Output
10 gathered: shards 8 + full buffers 0 = 8 elements 21 gathered: shards 8 + full buffers 8 = 16 elements 32 gathered: shards 8 + full buffers 16 = 24 elements

The largest module's forward/backward working set must still fit. deepspeed.zero.Init() can partition parameters during construction so initialization doesn't first materialize the entire model on every GPU, but it doesn't make an oversized live operator fit. Tiling or model parallelism may still be needed.[5]Reference 5ZeRO Configurationhttps://deepspeed.readthedocs.io/en/stable/zero3.html

External parameters and dynamic routing are different problems

A tied embedding reused outside its owning module is an external parameter. DeepSpeed can discover common cases automatically; otherwise use its explicit gathering/registration mechanisms. This coordinates access to an existing parameter.[5]Reference 5ZeRO Configurationhttps://deepspeed.readthedocs.io/en/stable/zero3.html

Dynamic routing is different. Two data ranks may select different existing experts and enter incompatible gather sequences. Marking the enclosing module as a ZeRO leaf gathers its descendants as a unit before executing that module, at the cost of a larger working set. This doesn't register newly created trainable parameters with an already-built optimizer.[3]Reference 3Training APIhttps://deepspeed.readthedocs.io/en/latest/training.html

Communication volume isn't elapsed time

A simplified ring model makes the paper's communication comparison concrete. For PPP communicated elements per full vector on NNN ranks, one all-gather or reduce-scatter sends (N−1)P/N(N-1)P/N(N−1)P/N elements per rank. Count sends or receives consistently, not their sum on one side of the comparison.

With equal element widths and no extra gathers, the basic schedules are:

ScheduleCollective workSent elements per rank
Replicated data parallelGradient reduce-scatter + all-gather2(N−1)P/N2(N-1)P/N2(N−1)P/N
ZeRO-2Gradient reduce-scatter + updated-parameter all-gather2(N−1)P/N2(N-1)P/N2(N−1)P/N
ZeRO-3Forward parameter gather + backward parameter gather + gradient reduce-scatter3(N−1)P/N3(N-1)P/N3(N−1)P/N

This explains the paper's approximately 2P2P2P versus 3P3P3P model for large NNN.[1]Reference 1ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.https://arxiv.org/abs/1910.02054 It doesn't promise that stage 3 takes 1.5 times as long. Different gradient/parameter dtypes, repeated gathers during accumulation or recomputation, parameter persistence, bucket sizes, overlap, and collective algorithms change the comparison. In stage 1, gradient storage stays replicated; don't infer a particular reduce-scatter implementation from its ownership formula.

On NVIDIA GPUs, NCCL commonly executes these collectives; DeepSpeed chooses the groups and schedules their use. A slow gather could be an exposed dependency, a small-message problem, or a network route problem. Read the per-rank timeline and communication counters together.

Offload exchanges memory pressure for movement

ZeRO-Offload moved optimizer-related state and work to CPU memory; ZeRO-Infinity extends the hierarchy to CPU and NVMe storage.[8]Reference 8ZeRO-Offload: Democratizing Billion-Scale Model Traininghttps://www.usenix.org/conference/atc21/presentation/ren-jie[9]Reference 9ZeRO-Infinity: Breaking the GPU Memory Wall for Extreme Scale Deep Learning.https://arxiv.org/abs/2104.07857 In current configuration, optimizer offload is available for stages 1 through 3, parameter offload for stage 3. Optimizer computation remains on the CPU even when its state is stored on NVMe.[5]Reference 5ZeRO Configurationhttps://deepspeed.readthedocs.io/en/stable/zero3.html

This fragment shows a stage-3 choice, not a complete launch configuration. It requires suitable local storage, available space, compatible I/O operators, and host memory for staging.

offload_fragment.json
1{ 2 "zero_optimization": { 3 "stage": 3, 4 "offload_optimizer": { "device": "cpu", "pin_memory": true }, 5 "offload_param": { 6 "device": "nvme", 7 "nvme_path": "/local_nvme/deepspeed" 8 } 9 } 10}

A bandwidth bound is a useful first check. If a step must move 12 GB through a bottleneck sustaining 6 GB/s, that path needs at least two seconds of service time. Perfect overlap with three seconds of independent compute could hide it; a dependency requiring the data before compute can't. Multiple reads/writes and shared-link contention can make the real step slower. These are illustrative values, not measured NVMe performance.

Measure GPU memory, host memory, pinned-memory use, CPU optimizer time, transfer volume, and storage queueing. Compare tokens per second at the same effective batch and sequence lengths. “It fits” answers a capacity question, not whether the training run finishes sooner.

Read the engine path, not just the config

The pinned source organizes the mechanisms as follows:[2]Reference 2DeepSpeed Source Repositoryhttps://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0

Entry pointWhat to inspect
deepspeed/__init__.pyInitialization and engine selection
deepspeed/runtime/engine.pyBackward, accumulation boundaries, step, save/load
deepspeed/runtime/zero/stage_1_and_2.pyGradient and optimizer ownership
deepspeed/runtime/zero/stage3.pySharded optimizer and parameter coordination
deepspeed/runtime/zero/partition_parameters.pyPartition storage, gathers, and parameter status
deepspeed/runtime/swap_tensor/Offload staging and state movement
deepspeed/runtime/pipe/engine.pyPipeline-specific scheduling and restrictions
Diagram showing initialize config + process groups, Selected engine, backward / step reduction + owned update, and module hooks gather + release.
initialize config + process groups, Selected engine, backward / step reduction + owned update, and module hooks gather + release.

Those branches are cooperating responsibilities, not three sequential phases. When a parameter is unexpectedly resident, follow its gather/release code; when a step occurs too early, follow accumulation bookkeeping.

Compatibility also belongs to a specific path. The pinned PipelineEngine rejects ZeRO-2 and ZeRO-3. Current AutoTP training documentation separately supports stages 0 through 3, with model-specific partition rules and topology-aware checkpoint handling. AutoTP isn't PipelineEngine, and neither claim establishes arbitrary combinations with other wrappers.[2]Reference 2DeepSpeed Source Repositoryhttps://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0[3]Reference 3Training APIhttps://deepspeed.readthedocs.io/en/latest/training.html

FSDP is another route to sharded training, using PyTorch-native interfaces rather than the same engine/config boundary. Megatron supplies model-parallel building blocks and schedules. Compare the integration your model requires, then test it; a feature list doesn't establish loss parity or checkpoint portability.

Resume training and export weights separately

A distributed checkpoint contains more than inference weights. Treat two outcomes separately:

OutcomeState neededVerification
Resume trainingParameters, optimizer, scheduler, counters, required RNG/data progressNext update agrees with an uninterrupted reference
Export a modelConsolidated weights plus model/tokenizer configurationReference inputs produce matching outputs within tolerance

Every participating process must call engine.save_checkpoint with a consistent tag. Calling it only on rank zero can hang. For a ZeRO-3 save/reload test, construct a fresh model and engine before loading; the pinned implementation warns against loading immediately into the same already-partitioned engine.[2]Reference 2DeepSpeed Source Repositoryhttps://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0

Use client_state for application-owned progress that DeepSpeed doesn't automatically manage. Restore the sampler or data-loader position and any required per-rank RNG state deliberately. A saved step counter alone doesn't ensure the next microbatch is the same.

For supported dense ZeRO layouts, get_fp32_state_dict_from_zero_checkpoint reconstructs FP32 weights. Plan CPU memory for consolidation; lazy conversion changes when tensors are materialized, not the size of the final weights. A weights-only export doesn't contain enough state to reproduce the next Adam update.[10]Reference 10DeepSpeed Model Checkpointinghttps://deepspeed.readthedocs.io/en/latest/model-checkpointing.html

Universal Checkpointing adds a conversion/load path for supported topology changes, not arbitrary model conversion. The current tutorial requires compatible parameter names and shapes and documents separate AutoTP/AutoEP layouts. In particular, partition-native AutoEP checkpoints aren't supported by zero_to_fp32.py; the documented path uses ds_to_universal.py instead.[11]Reference 11Universal Checkpointing with DeepSpeed: A Practical Guide.https://www.deepspeed.ai/tutorials/universal-checkpointing/

Before a long run, save at the intended topology, stop every worker, reload into a fresh run, and compare the next loss, update, scheduler state, and data progress. Test export separately. These distributed recovery operations weren't executed here.

Project context and contribution path

The original ZeRO authors worked at Microsoft. The project's pinned governance defines technical oversight through a Technical Steering Committee and its relationship with Linux Foundation AI & Data.[1]Reference 1ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.https://arxiv.org/abs/1910.02054[12]Reference 12DeepSpeed Project Charter and Governancehttps://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/GOVERNANCE.md The pinned committer roster includes contributors affiliated with Snowflake, Microsoft, Anyscale, AMD, Google, Intel, UIUC, and Argonne National Laboratory. Those affiliations describe the dated roster, not everyone's current employer.[13]Reference 13DeepSpeed TSC Committershttps://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/COMMITTERS.md

The code license is Apache-2.0. The charter specifies Developer Certificate of Origin sign-off for contributions and CC BY 4.0 for project documentation.[14]Reference 14DeepSpeed Apache License 2.0https://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/LICENSE[12]Reference 12DeepSpeed Project Charter and Governancehttps://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/GOVERNANCE.md Check the current contribution instructions before submitting a change.

For a source-level investigation, reduce the failing run first. Include the version/commit, resolved config, smallest reproducer, topology, and earliest rank error. Report whether the failure is initialization, correctness, performance, or recovery; a peer's collective timeout can be a consequence of an earlier failure elsewhere.

Turn a failed run into a specific hypothesis

Common failure modes

ObservationNext investigation
Longer sequences OOM despite a small state estimateSeparate saved activations and workspaces from model state
OOM during a stage-3 gatherCount simultaneous gathered buffers, padding, and prefetch
Loss changes after scaling outCheck effective batch, token weighting, precision, and update count
Hang with routed expertsCompare gather order and the configured leaf boundary
Lower GPU peak but slower steps after offloadMeasure exposed transfers and CPU/storage service time
Saved directory exists but resume divergesRestore optimizer, scheduler, RNG, and data progress in a fresh engine

Evaluation rubric

  • Derive each stage's storage from tensor ownership and dtype, then explain what the estimate excludes.
  • Trace one parameter and its gradient through gather, compute, reduction, and the accumulation-boundary update.
  • Separate CPU reference checks from GPU measurements, and distinguish a resumable checkpoint from a weights-only export.

Follow-up questions

The state estimate fits, but enabling prefetch causes an OOM. Why isn't that a contradiction?

Answer

The estimate counts persistent state. Prefetch can keep multiple full gathered tensors live alongside their shards. Add the simultaneous allocations, activations, and workspaces before comparing with available device memory.

Two ranks route to different experts. Should you create parameters lazily and register them as external parameters?

Answer

No. The routing problem concerns compatible gathers for existing submodules. A suitable leaf boundary gathers descendants together, with a memory cost. External-parameter coordination concerns access outside an existing parameter's owning module; it doesn't make post-initialization parameter creation safe for the optimizer.

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.Using two-byte parameters, four-byte gradients, and twelve-byte optimizer-plus-master state, what is the idealized ZeRO-3 storage per parameter on four ranks?

Correct answer: 4.5 bytes

The recipe totals 18 bytes, divided across four ranks. A BF16 model doesn't imply that every gradient buffer also uses two bytes.

2.Why must a sharded Adam owner reduce rank gradients before updating its second moment?

Correct answer: The square of the mean differs from the mean of squares.

Adam's second moment uses the squared reduced gradient. Updating moments from local gradients first generally changes the algorithm.

3.A rank retains eight shard elements and allocates a separate eight-element full gather. How many parameter elements coexist in this model?

Correct answer: Sixteen, because both allocations remain live.

Logical overlap doesn't imply storage aliasing. The full gather contains a copy of the local values as well as remote values.

4.A job has eight GPUs with tensor-parallel degree two and data-parallel degree four. Microbatch size is two and accumulation is eight. What is its global batch?

Correct answer: 64 samples

Independent data replicas determine the multiplier: 2 × 8 × 4. Tensor-parallel ranks cooperate on the same examples.

5.A configured reduce bucket holds 200 million elements. What determines its byte footprint?

Correct answer: Element count times the actual buffer element width.

The bucket field counts elements. Its storage is 400 MB at two bytes per element or 800 MB at four, before other allocations.

6.Different data ranks activate different existing experts and hang in gathers. What does a ZeRO leaf boundary change?

Correct answer: It gathers descendants together, increasing the possible working set.

A leaf boundary coordinates parameter availability for dynamic selection. Gathering all descendants doesn't require executing every expert.

7.A ring model predicts 1.5 times the sent elements for ZeRO-3 versus replicated data parallelism. What follows about step time?

Correct answer: Time still depends on dtype, scheduling, overlap, and bandwidth.

Volume is only one input to time. The ratio also assumes a specific gather/reduction schedule and equal communicated element widths.

8.What is wrong with calling DeepSpeed save_checkpoint only on rank zero?

Correct answer: Other ranks must participate; the call can otherwise hang.

Checkpointing is a distributed engine operation with rank-owned state and synchronization, not a rank-zero-only file write.

9.A consolidated FP32 model matches reference outputs. What remains unverified?

Correct answer: Whether optimizer and data progress resume the next update.

Output parity tests the model export. Exact training continuation also needs optimizer, scheduler, RNG, and data-progress handling.

10.Current AutoTP documentation includes ZeRO-3, while the pinned PipelineEngine rejects it. How should you choose a configuration?

Correct answer: Identify the actual engine, topology, model rules, and installed version.

These are different runtime paths. A supported stage in one path doesn't establish compatibility in another.

10 questions remaining.

Next Step
Continue to Deep Dive - Ray

DeepSpeed coordinates tensors and optimizer state within a distributed training job. Ray adds scheduling for tasks, actors, and workflows across a cluster.

PreviousDeep Dive - Megatron
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.

Rajbhandari, S., et al. · 2020 · SC 2020

https://arxiv.org/abs/1910.02054

DeepSpeed Source Repository

DeepSpeed Contributors · 2026

https://github.com/deepspeedai/DeepSpeed/tree/c455031422641a825588c926455458c82b27b6c0

Training API

DeepSpeed Team · 2026

https://deepspeed.readthedocs.io/en/latest/training.html

DeepSpeed Configuration JSON

DeepSpeed Contributors · 2026

https://www.deepspeed.ai/docs/config-json/

ZeRO Configuration

DeepSpeed Team · 2026

https://deepspeed.readthedocs.io/en/stable/zero3.html

Adam: A Method for Stochastic Optimization.

Kingma, D. P., Ba, J. · 2015 · ICLR 2015

https://arxiv.org/abs/1412.6980

DeepSpeed Installation Details

DeepSpeed Team · 2026

https://www.deepspeed.ai/tutorials/advanced-install/

ZeRO-Offload: Democratizing Billion-Scale Model Training

Ren, J., Rajbhandari, S., Aminabadi, R. Y., et al. · 2021 · USENIX ATC 2021

https://www.usenix.org/conference/atc21/presentation/ren-jie

ZeRO-Infinity: Breaking the GPU Memory Wall for Extreme Scale Deep Learning.

Rajbhandari, S., et al. · 2021 · SC 2021

https://arxiv.org/abs/2104.07857

DeepSpeed Model Checkpointing

DeepSpeed Contributors · 2026

https://deepspeed.readthedocs.io/en/latest/model-checkpointing.html

Universal Checkpointing with DeepSpeed: A Practical Guide.

DeepSpeed Team · 2026

https://www.deepspeed.ai/tutorials/universal-checkpointing/

DeepSpeed Project Charter and Governance

DeepSpeed Project · 2026

https://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/GOVERNANCE.md

DeepSpeed TSC Committers

DeepSpeed Project · 2026

https://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/COMMITTERS.md

DeepSpeed Apache License 2.0

DeepSpeed Project · 2026

https://github.com/deepspeedai/DeepSpeed/blob/c455031422641a825588c926455458c82b27b6c0/LICENSE

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.