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
LearnInference & Production ScaleDistributed Inference Data Plane
🚀HardInference Optimization

Distributed Inference Data Plane

Follow an 8K prompt through rank collectives and KV handoff, calculate transfer and memory budgets, and test why expired buffers aren't immediately reusable.

20 min read
Learning path
Step 164 of 196 in the full curriculum
GPU Failure OperationsServing Benchmark Engineering

Personalize this lesson

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

Request req-8k-017 contains 8,192 prompt tokens. While one replica processes that prompt, already-streaming replies may wait for their next decode step. Moving prefill to a separate worker can reduce this interference, but creates another wait: the prompt's key-value (KV) cache must reach the decoder. For the teaching model below, that's 2.50 GiB, not a small routing message.

A fast copy to an incompatible decoder is useless. A correct copy into a buffer that's already been reassigned is dangerous. Distributed inference has to coordinate both the movement of bytes and the right to use them.

Model parallelism explained how ranks cooperate inside a model replica. KV Cache and PagedAttention explained cached token state and logical blocks. The GPU failure operations lesson covered worker failures. Here those pieces meet in one request's prefill-to-decode handoff.

Decisions, tensor traffic, and token streams

The control plane chooses workers, checks caller permissions, reserves capacity, and assigns request ownership. The data plane executes the model and carries its prompt, intermediate tensors, KV state, and output. These names describe responsibilities, not whole processes: a router makes placement decisions and also forwards request bytes.

Our request uses prefill group P7 and decode group D3, each with two tensor-parallel (TP) ranks. Communication inside either group combines partial model results. Communication between the groups transfers request state. These are different operations even if they share the same physical network.

Two TP ranks inside P7 exchange collective results, as do two ranks inside D3. Separate horizontal transfers carry 1.25 GiB KV shards from P7:0 to D3:0 and P7:1 to D3:1. The control lane reserves D3 and assigns one stream owner.
Vertical links join ranks executing one model phase. Horizontal links carry distinct KV shards between phases. Moving a shard isn't an all-reduce: the receiver preserves its values and logical positions.

Splitwise and DistServe study separating prefill and decode so their resources and latency objectives can be managed independently.[1]Reference 1Splitwise: Efficient Generative LLM Inference Using Phase Splitting.https://arxiv.org/abs/2311.18677[2]Reference 2DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving.https://arxiv.org/abs/2401.09670 A split isn't automatically faster. It adds transfer, coordination, and separately provisioned pools; a tuned mixed pool with chunked prefill may already meet the same objectives. vLLM still labels its disaggregated-prefill feature experimental.[3]Reference 3Disaggregated Prefillinghttps://github.com/vllm-project/vllm/blob/main/docs/features/disagg_prefill.md

Collectives inside a replica must agree

Suppose one layer produces partial activation vectors [1,2] on rank 0 and [10,20] on rank 1. A sum all-reduce gives [11,22] to both ranks. An all-gather instead concatenates contributions in rank order. Reduce-scatter sums corresponding elements and gives each rank only its assigned output chunk.[4]Reference 4NCCL Collective Operationshttps://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/collectives.html

The CPU example distinguishes these contracts. The sequence labels are application-level diagnostics, not fields NVIDIA Collective Communications Library (NCCL) checks for you.

collective-contracts.py
1partials = [[1, 2], [10, 20]] 2reduced = [sum(column) for column in zip(*partials, strict=True)] 3all_reduce = [reduced.copy() for _ in partials] 4reduce_scatter = [[reduced[0]], [reduced[1]]] 5all_gather = [value for shard in reduce_scatter for value in shard] 6assert all_reduce == [[11, 22], [11, 22]] 7assert all_gather == reduced 8print("all-reduce:", all_reduce) 9print("reduce-scatter:", reduce_scatter, "then gather:", all_gather) 10 11def matching_schedule(rank_logs): 12 return bool(rank_logs) and all(log == rank_logs[0] for log in rank_logs) 13 14# (model step, layer, operation, element count, dtype) 15a = (0, 3, "all_reduce_sum", 2, "float32") 16b = (0, 4, "all_reduce_sum", 2, "float32") 17assert matching_schedule([[a, b], [a, b]]) 18assert not matching_schedule([[a, b], [b, a]]) 19assert not matching_schedule([[a, b], [a]]) 20wrong_count = (0, 3, "all_reduce_sum", 3, "float32") 21assert not matching_schedule([[a], [wrong_count]]) 22wrong_dtype = (0, 3, "all_reduce_sum", 2, "float16") 23assert not matching_schedule([[a], [wrong_dtype]]) 24print("reordered, missing, mismatched-count and mismatched-dtype calls: detected")
Output
1all-reduce: [[11, 22], [11, 22]] 2reduce-scatter: [[11], [22]] then gather: [11, 22] 3reordered, missing, mismatched-count and mismatched-dtype calls: detected

For a given collective, participating ranks must agree on the operation and its arguments, including element count and dtype. They must issue matching collective sequences. Swapping two same-shaped reductions can combine the wrong layers without an obvious shape error; mismatches can also hang or corrupt results. NCCL's group-call documentation makes the ordering requirement explicit.[4]Reference 4NCCL Collective Operationshttps://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/collectives.html[5]Reference 5NCCL Group Callshttps://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/groups.html

A successful NCCL enqueue isn't a completed GPU result. The consumer needs the appropriate CUDA stream dependency or completion event.[6]Reference 6NCCL CUDA Stream Semanticshttps://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/streams.html Cancelling a request on just one rank mustn't make that rank skip a collective that its peers still execute. Coordinate scheduling changes across the group; a failed group needs the runtime's communicator recovery procedure, not an independent retry of one rank's next collective.

The script checks arithmetic and an abstract schedule. It doesn't launch NCCL, detect a real distributed hang, or validate CUDA stream synchronization.

How much state crosses the handoff?

Keep req-8k-017 fixed. Its hypothetical decoder-only model has 80 layers, 8 KV heads per layer, head dimension 128, and 2-byte BF16 cache values. Every cached token stores both a key and a value:

Btoken=2×80×8×128×2=327,680 bytes=320 KiB.B_{token}=2 \times 80 \times 8 \times 128 \times 2 =327{,}680\text{ bytes}=320\text{ KiB}.Btoken​=2×80×8×128×2=327,680 bytes=320 KiB.

Multiplying by 8,192 tokens gives 2,684,354,560 bytes, or 2.50 GiB. Under ideal two-way KV-head sharding, each rank holds 1.25 GiB. Both shards must arrive, so TP=2 doesn't halve the model-wide payload. Replicated heads, padding, quantization metadata, or layout conversion can change physical bytes. This formula isn't a universal cache layout for every architecture.

At 16 tokens per logical block, the prompt occupies 512 blocks. Each model-wide block accounts for 5 MiB. Reserving another 128 token positions adds 40 MiB, giving 2,726,297,600 bytes before allocator and other runtime overhead. A partial final block rounds allocation up; the payload of real tokens and the reserved block capacity are different quantities.

The exercise separates those two quantities and uses assumed, not measured, effective bandwidth. GB/s uses decimal bytes; GiB uses powers of 1,024.

kv-budget.py
1BYTES_PER_TOKEN = 2 * 80 * 8 * 128 * 2 2BLOCK_TOKENS = 16 3 4def reserved_bytes(prompt_tokens, output_tokens): 5 if prompt_tokens < 0 or output_tokens < 0: 6 raise ValueError("negative token count") 7 blocks = (prompt_tokens + output_tokens + BLOCK_TOKENS - 1) // BLOCK_TOKENS 8 return blocks * BLOCK_TOKENS * BYTES_PER_TOKEN 9 10payload = 8192 * BYTES_PER_TOKEN 11reservation = reserved_bytes(8192, 128) 12assert payload == 2_684_354_560 13assert reservation == 2_726_297_600 14assert reserved_bytes(8193, 128) - reservation == 16 * BYTES_PER_TOKEN 15assert reserved_bytes(0, 0) == 0 16capacity = 8 * 1024**3 17assert capacity // reservation == 3 18print(f"payload: {payload / 1024**3:.2f} GiB; reservation: {reservation / 1024**2:.0f} MiB") 19print("8 GiB capacity fits", capacity // reservation, "requests before other overhead") 20for assumed_gb_s in (12.5, 25.0, 50.0): 21 milliseconds = payload / (assumed_gb_s * 1e9) * 1000 22 print(f"assumed {assumed_gb_s:4.1f} GB/s -> {milliseconds:6.1f} ms")
Output
1payload: 2.50 GiB; reservation: 2600 MiB 28 GiB capacity fits 3 requests before other overhead 3assumed 12.5 GB/s -> 214.7 ms 4assumed 25.0 GB/s -> 107.4 ms 5assumed 50.0 GB/s -> 53.7 ms

For serial transfer with aggregate effective payload rate RRR, the transfer term is B/RB/RB/R. At an assumed 25 GB/s, it's 107.4 ms. If two independent links each carry one 1.25 GiB shard concurrently at 25 GB/s, the transfer finishes in roughly 53.7 ms before overhead. If both streams share a 25 GB/s bottleneck, dividing each shard by 25 GB/s falsely doubles the available capacity.

Reserve capacity before creating expensive state

D3 has 8 GiB of available KV capacity in our simplified budget. Three requests fit; four don't. A request counter alone misses that limit: one 100-token prompt and one 8K prompt each count as one request but consume very different memory.

Our teaching protocol reserves downstream capacity before prefill starts. This reduces wasted prompt computation when decode is full. It's a design choice, not a claim that every engine implements this exact sequence. Mooncake discusses early rejection and KV-aware scheduling to avoid completing prefill for work that can't be served.[7]Reference 7Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving.https://arxiv.org/abs/2407.00079

Admission must consider several constraints together:

ResourceChargeWhen new work waits or rejects
Prefill workPrompt tokens or estimated computeBounded prefill queue is full or deadline is implausible
Destination memoryRounded prompt plus output KV bytesReservation would exceed allocatable capacity
Transfer pathBytes in flight and queue age per shared pathTransport queue or its deadline budget is exhausted
Decode executionActive sequences and expected output workMemory fits but token-step latency would suffer

An output reservation is a budget, not a prediction that generation ends there. Enforce an output cap or acquire more credit before crossing the reserved boundary. Cancellation, timeout, and completed requests must eventually release their charges, but only after the associated memory is safe to reuse.

A lease limits how long a reservation may become ready. Its expiry uses the destination's monotonic clock; the sender doesn't get to extend it by supplying a larger timestamp. After commit, active request ownership replaces this admission lease. Expiry revokes admission immediately. It doesn't prove that remote writes have stopped. Retired buffers can remain physically charged while their transfers drain.

Agree on the interpretation before moving bytes

The destination first compares the sender's manifest against a trusted reservation. The manifest describes the transfer; it doesn't authorize itself.

Identity or shapeRunning valueWhy it matters
Request and attemptreq-8k-017, attempt 4Reject a delayed completion from superseded attempt 3
Tenant or sharing scopetenant-73Prevent unauthorized reuse of another tenant's state
Model tuplem42, tokenizer t17, no adapterMatch weights, token interpretation, and adapter
Prompt and positionToken digest tok-bf81, positions 0 through 8191Match the exact canonical token sequence and positions
KV interpretationBF16, hnd-v3, block size 16Interpret bytes with the correct dtype, strides, and block layout
Shard coverageRanks 0 and 1, logical blocks 0 through 511Require every needed layer, head shard, and token range
Destination reservationd3-lease-772Bind the transfer to capacity and a local expiry

The short digests here are labels, not cryptographic examples. Use canonical token IDs from one owner rather than independently rendering chat templates at both workers. vLLM documents experimental reuse of prefill token IDs through kv_transfer_params for this purpose.[3]Reference 3Disaggregated Prefillinghttps://github.com/vllm-project/vllm/blob/main/docs/features/disagg_prefill.md Sampling state, the last token or hidden state needed to start decode, and first-token ownership also belong in the engine-specific handoff. KV tensors alone aren't necessarily enough to resume generation.

Logical block 31 can live at physical block 904 in P7 and block 118 in D3. Local addresses needn't match. The mapping from logical positions to the correct destination shard must match. A connector may explicitly support repartitioning or layout conversion; budget and validate that conversion rather than silently reinterpreting bytes.

A transfer engine doesn't own the request

NIXL separates memory registration, connection-metadata exchange, buffer descriptors, and asynchronous transfer operations. An external conductor coordinates the workload and metadata path.[8]Reference 8NVIDIA Inference Transfer Library: Architecture and API Flowhttps://github.com/ai-dynamo/nixl/blob/main/docs/nixl.md Serialize that small metadata, not gigabytes of tensors into JSON. Keep the payload in registered GPU or host memory supported by the connector.

The surrounding service still owns authorization, placement, retry policy, and output ownership. Protect the metadata channel, restrict descriptor exposure, and isolate registered regions. Remote keys and descriptors don't belong in application logs. RDMA isn't itself a promise of encryption or tenant isolation. Transport integrity and authenticated workload identity are separate from checking the model and layout fields.

Dynamo's documented flow returns backend-specific handoff metadata from prefill and uses it to coordinate decode and NIXL movement.[9]Reference 9Disaggregated Servinghttps://docs.nvidia.com/dynamo/dev/knowledge-base/concepts/system-architecture/disaggregated-serving Its router can constrain decode placement to a compatible topology domain, such as a rack or zone.[10]Reference 10Disaggregated Serving: Prefill and Decode Routinghttps://docs.nvidia.com/dynamo/dev/knowledge-base/modular-components/router/disaggregated-serving A lightly loaded decoder across a congested path can finish later than a busier nearby one. Evaluate the whole path, including GPU-to-NIC placement, staging, registration, conversion, and shared link contention.

NIXL's backend guide gives two especially relevant limits: distinct transfer requests aren't automatically ordered, and the application must prevent simultaneous transfers from corrupting the same region. Releasing a transfer can involve asynchronous cancellation that hasn't yet succeeded.[11]Reference 11NIXL Backend Guidehttps://github.com/ai-dynamo/nixl/blob/main/docs/BackendGuide.md Treat “cancel requested,” “operation stopped,” and “buffer reusable” as different events.

Timeout revokes ownership, not physical access

Suppose 70% of attempt 4's KV has arrived when its lease expires. The destination must reject commit. But returning those blocks to the allocator immediately could let an old remote write overwrite attempt 5's cache.

Five ordered events: transfer starts, lease expires, a late write arrives, accesses stop, and memory is reclaimed. Attempt 4 cannot commit after expiry, but the buffer remains charged through quiescence. Only reclamation returns the byte credit.
Logical retirement happens at expiry. Physical reclamation waits until the transport and any GPU consumers can no longer access the buffer. A new attempt needs separate capacity during that interval.

A conservative whole-prompt protocol has four distinct boundaries:

  1. Reserve: charge capacity and record the expected manifest under a server-issued attempt.
  2. Transfer: keep incoming blocks unavailable to attention, even if some are complete.
  3. Commit: after compatibility, complete unique coverage, integrity, and connector-required visibility checks, grant one decoder ownership.
  4. Retire and reclaim: stop admission and streaming, then wait for successful cancellation or completion and GPU-reader quiescence before reusing memory.

Advanced connectors can overlap layer-wise transfer and compute. That requires dependency tracking for each consumed region; it doesn't permit attention to read a missing region. The whole-prompt gate here is intentionally simpler.

A duplicate arrival mustn't count as a new block. Counting 1,024 notifications isn't equivalent to receiving 512 distinct blocks on each of two ranks. Likewise, a completed byte count doesn't validate layer coverage or a manifest's interpretation.

Execute the ownership rules locally

Download handoff_model.py. It serializes transitions in one Python process and stores block identities, not tensor data. The expected manifest comes from its trusted reservation; a received manifest must match it. finish(), abort_ack(), and consumer_stopped() represent trusted runtime evidence, not measurements that this script obtains.

This short run expires a lease while writes are still possible. Credit remains unavailable until successful abort acknowledgement.

expire-without-reusing.py
1from assets.handoff_model import Manifest, Receiver, rejected 2 3manifest = Manifest() 4pool = Receiver(manifest.reserved) 5key = pool.reserve(manifest, now=0, ttl=10) 6pool.begin(key, manifest) 7pool.arrive(key, rank=0, block=0) 8rejected(lambda: pool.commit(key, now=10)) # exact expiry boundary 9pool.arrive(key, rank=1, block=0) # late write remains possible 10rejected(lambda: pool.reclaim(key)) 11print("after expiry, free bytes:", pool.free) 12pool.abort_ack(key) # assumed connector confirmation 13pool.reclaim(key) 14pool.reclaim(key) # duplicate cleanup is harmless 15print("after quiescence, free bytes:", pool.free)
Output
1after expiry, free bytes: 0 2after quiescence, free bytes: 2726297600

The longer checks reject eight manifest mismatches, exercise duplicate and missing blocks, reject streaming before commit, retain an old attempt's charge during replacement, and prevent retries after visible output. They also enumerate 720 orderings of two arrivals, completion, commit, cancellation, and reclamation.

check-handoff-interleavings.py
1from assets.handoff_model import run_checks 2 3run_checks()
Output
18 manifest mismatches, duplicate/missing blocks, stale attempts and expiry: pass 2720 event orders: no partial commit or premature credit reuse

These checks validate the model's transitions. They don't establish distributed atomicity, authentication, memory visibility, or cancellation semantics in NIXL, vLLM, Dynamo, CUDA, or an RDMA backend. A deployed coordinator needs authoritative attempt ownership, serialization or compare-and-swap at commit, durable recovery rules, and a frontend that fences stale streams. A higher attempt number in a message alone doesn't stop an old writer or stream owner.

Failure modes and retry boundaries

Before output is visible, the service may retry if it can fence the old owner, preserve request semantics, and stay within its deadline and retry budget. This still costs compute and network traffic. Current vLLM configuration exposes fail and recompute policies for KV-load failure; those are local engine behaviors, not a complete resumable-stream protocol.[12]Reference 12KVTransferConfig API Referencehttps://docs.vllm.ai/en/latest/api/vllm/config/kv_transfer/

After output may have reached the client, don't transparently restart at token 1. A lost acknowledgement can make delivery uncertain. Exact continuation requires an agreed cursor, token or frame deduplication, sampling state, and authoritative ownership. Without that protocol, return an explicit interrupted-stream error.

FailureSafe recovery boundary
Prefill failsFence that attempt; recompute on a healthy group if retry policy permits
Partial transfer times outRetire it; reclaim only after its accesses stop; retransmit retained source KV or recompute
One decode rank failsStop scheduling the affected model group; recover its collective state before reuse
Commit acknowledgement is lostQuery the authoritative state; don't infer “not committed” from timeout
Decode dies after visible outputSurface a stream error unless explicit resume semantics exist
Client cancelsStop new work, fence output, drain accesses, and release each owner's resources idempotently

Retaining source KV briefly after commit can make retransmission cheaper, but consumes prefill memory. The retained copy needs its own accounted capacity and expiry. Never release a source buffer while a transport may still read it. NIXL's remote-agent invalidation helps manage disappearing peers, but the application still has to coordinate safe buffer lifetime.[8]Reference 8NVIDIA Inference Transfer Library: Architecture and API Flowhttps://github.com/ai-dynamo/nixl/blob/main/docs/nixl.md

Measure the critical path, not a sum of unrelated percentiles

For one request without overlap, time to first token (TTFT) is the sum of its edge, routing, queue, prefill, handoff, and first-token durations. For overlapping spans, use the union of their time intervals on the dependency path. Don't count prefill again inside a handoff term that already includes it.

The synthetic timeline assumes prefill runs from 0 to 310 ms. Transfer either starts afterward or starts at 240 ms and overlaps its final 70 ms. Commit plus the first decode step takes 28 ms after both finish. No network measurements are implied.

handoff-critical-path.py
1prefill_end = 310.0 2transfer_ms = 2_684_354_560 / 25e9 * 1000 3commit_and_first_token = 28.0 4for transfer_start in (310.0, 240.0): 5 ready = max(prefill_end, transfer_start + transfer_ms) 6 first_token = ready + commit_and_first_token 7 print(f"transfer starts {transfer_start:.0f} ms: first token at {first_token:.1f} ms") 8assert abs((310 + transfer_ms + 28) - (240 + transfer_ms + 28) - 70) < 1e-9
Output
1transfer starts 310 ms: first token at 445.4 ms 2transfer starts 240 ms: first token at 375.4 ms
Shared 0 to 500 millisecond axes compare two assumed schedules. Prefill is 0 to 310 ms in both. Serial transfer runs 310 to 417.4, then first-token work ends at 445.4. Overlapped transfer runs 240 to 347.4, then first-token work ends at 375.4. Savings are 70 ms. Bars show active intervals; rings mark completion.
The overlap saves 70 ms on this assumed critical path. It doesn't remove transfer bytes or guarantee spare bandwidth for other requests. Edge and queue delays are omitted from both rows.

Phase p99 values don't generally add to end-to-end p99: the slowest prefill and slowest transfer may belong to different requests. Compute end-to-end quantiles from joined request traces. Keep request IDs in traces, not unbounded metric labels.

SignalDenominator or unitQuestion it answers
Transfer payload rateUnique completed payload bytes / transfer durationHow quickly did useful KV arrive?
Fabric bytesBytes crossing the measured linkDid retries, replication, or protocol overhead add traffic?
Quarantined KVBytes retired but not yet safe to reuseIs cancellation or cleanup reducing capacity?
Wasted prefillPrefill tokens for requests that never commit / all prefill tokensIs admission too late or handoff unreliable?
TTFT and decode attainmentSuccessful requests meeting each target / offered requestsHow much offered work meets the user-facing objectives?
Rejection and error rateRejected or failed requests / offered requestsIs apparently good latency hiding dropped work?

A host staging copy increases memory-system traffic, but doesn't necessarily increase network wire bytes. Specify where every byte counter is measured. Likewise, reduced handoff bandwidth may raise TTFT without changing steady-state inter-token latency (ITL), but shared-fabric contention can also slow collectives and hurt ITL. Treat that symptom as a clue, not proof.

Compare a mixed pool and a split pool under the same offered workload, output lengths, hardware budget, and SLO definitions. Report goodput, rejection, errors, and tail latency together. A service that rejects almost everything can have excellent latency for the few requests it accepts.

Try a changed workload

Follow-up questions

Prefill can finish 40 of these 8K requests per second, but all KV transfers share an effective 25 GB/s path. Can the transfer queue remain stable at that arrival rate?

Answer

Each request moves 2,684,354,560 bytes, so 40 requests/s demands about 107.4 GB/s. Even before other overhead, the 25 GB/s path supports only about 9.31 such transfers/s. A finite queue can absorb a short burst, not this sustained mismatch. Admission must slow or reject work, or the system needs less transferred state or more independent bandwidth. Faster prefill alone doesn't resolve it.

The prompt grows from 8,192 to 8,193 tokens while the 128-position output budget stays fixed. How much additional KV capacity does the 16-token block allocator reserve?

Answer

The total grows from 8,320 positions, exactly 520 blocks, to 8,321 positions, which requires 521 blocks. One model-wide block is 16 × 320 KiB = 5 MiB. Reserved capacity rises from 2,600 to 2,605 MiB, although the additional real token contains only 320 KiB of KV. The allocation bound and logical payload bound serve different purposes.

Evaluation rubric

  • Calculate both logical KV payload and rounded allocation; distinguish aggregate link capacity from each stream's requested rate.
  • Trace a timed-out attempt through fencing, late writes, quiescence, and reclamation without returning its byte credit early.
  • Explain which guarantees the local model checks and which require actual connector, GPU, and coordinator evidence.
Complete the lesson

Mastery Check

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

1.What does a sum all-reduce return for rank inputs [1,2] and [10,20]?

Correct answer: [11,22] on both ranks

All-reduce combines corresponding elements and returns the whole result to every rank. Concatenation is all-gather; partitioning the sum is reduce-scatter.

2.Two TP ranks issue same-shaped reductions for layers 3 and 4 in opposite orders. What should the scheduler do?

Correct answer: Restore a matching operation sequence across the ranks

Collective order carries meaning. Same-shaped operations from different layers can match incorrectly, and other mismatches may hang. Request labels don't repair the operation sequence.

3.An 8,192-token prompt uses 320 KiB of KV per token. Under ideal TP=2 sharding, what must the full request transfer?

Correct answer: 2.50 GiB total, with 1.25 GiB per rank

8,192 times 327,680 bytes is 2,684,354,560 bytes. Sharding divides ownership, not the model-wide payload.

4.Two 1.25 GiB shards share a single 25 GB/s bottleneck. What is their ideal total transfer time without other overhead?

Correct answer: About 107 ms because both shards consume the shared link

Divide the combined 2,684,354,560 bytes by 25 billion bytes per second. The 54 ms case requires two independent 25 GB/s paths, not two streams on one bottleneck.

5.Why reserve output KV capacity as well as prompt KV capacity?

Correct answer: Decode adds cached positions as generation proceeds

The decoder needs room to grow the cache. Enforce an output cap or acquire more credit before exceeding the reservation; execution slots and network capacity remain separate limits.

6.A lease expires while a remote KV write may still be in flight. When may the allocator reuse its destination buffer?

Correct answer: After retirement and confirmation that all accesses stopped

Fencing prevents logical commit, not physical writes. Keep the buffer charged and quarantined until the transport and any GPU readers are quiescent.

7.The receiver expects 512 blocks on each of two ranks and observes 1,024 arrival notifications. Is completeness established?

Correct answer: No, it needs complete unique rank-and-block coverage

Duplicate notifications can hide missing blocks. Logical coverage must match the trusted manifest, along with interpretation, integrity, and visibility requirements.

8.The router loses a decode-commit acknowledgement. What should it infer?

Correct answer: The outcome is uncertain and needs authoritative resolution

An acknowledgement can be lost after commit. Query authoritative state and fence ownership before recovery; timeout doesn't establish whether commit or visible output happened.

9.The CPU handoff model passes all 720 event orders. What has been verified?

Correct answer: The modeled transitions preserve their checked invariants

The script assumes serialized state changes and trusted quiescence callbacks. It doesn't execute or validate a network, GPU, or distributed coordinator.

10.How should end-to-end p99 TTFT be calculated?

Correct answer: Take the p99 of each request's joined critical-path duration

Phase tails may belong to different requests, and some phases overlap. Join each request's spans first, then compute the end-to-end distribution and report offered-work failures separately.

10 questions remaining.

Next Step
Continue to Serving Benchmark Engineering

Turn these byte counts, ownership boundaries, and latency spans into a controlled load experiment that measures how much offered work meets the serving objectives.

PreviousGPU Failure Operations
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Splitwise: Efficient Generative LLM Inference Using Phase Splitting.

Patel, P., et al. · 2023

https://arxiv.org/abs/2311.18677

DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving.

Zhong, Y., et al. · 2024 · OSDI 2024

https://arxiv.org/abs/2401.09670

Disaggregated Prefilling

vLLM Project · 2026

https://github.com/vllm-project/vllm/blob/main/docs/features/disagg_prefill.md

NCCL Collective Operations

NVIDIA · 2026

https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/collectives.html

NCCL Group Calls

NVIDIA · 2026

https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/groups.html

NCCL CUDA Stream Semantics

NVIDIA · 2026

https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/streams.html

Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving.

Qin, Y., et al. · 2024

https://arxiv.org/abs/2407.00079

NVIDIA Inference Transfer Library: Architecture and API Flow

NIXL Project · 2026

https://github.com/ai-dynamo/nixl/blob/main/docs/nixl.md

Disaggregated Serving

NVIDIA Dynamo Project · 2026

https://docs.nvidia.com/dynamo/dev/knowledge-base/concepts/system-architecture/disaggregated-serving

Disaggregated Serving: Prefill and Decode Routing

NVIDIA Dynamo Project · 2026

https://docs.nvidia.com/dynamo/dev/knowledge-base/modular-components/router/disaggregated-serving

NIXL Backend Guide

NVIDIA · 2026

https://github.com/ai-dynamo/nixl/blob/main/docs/BackendGuide.md

KVTransferConfig API Reference

vLLM Project · 2026

https://docs.vllm.ai/en/latest/api/vllm/config/kv_transfer/

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.