LLMModule 09 · Problems 23–25

MoE serving, cutting cost and the 1M-user capstone

The last three problems are about scale and money. How do you split a 2.8-trillion-parameter mixture-of-experts model across GPUs? How do you cut the cost per token by 70% without making answers worse? And how many GPUs, and how many dollars, does a chat service for a million daily users need? The answers are estimates built from stated assumptions, and the skill being tested is making those assumptions visible.

Problems covered23 (MoE parallelism and all-to-all) · 24 (cut cost per million tokens by 70%) · 25 (design for 1M daily active users)
Interview levelSenior. Problem 25 is a common system-design round at every level.
Key ideasExpert parallelism, all-to-all, PP × EP layouts, cost per token, stacking multipliers, peak QPS, Little's law, provisioned vs autoscaled cost
Time to prep4–5 h: 1.5 h on MoE layouts, 1 h on cost levers, 1.5 h rehearsing the capstone estimate with the calculator

Source: Vishal Singh's post of 25 inference interview problems, problems 23–25 · saved in sources/09-vishal-inference-25.md

Start here (no maths needed)

Big AI models are expensive to run. Engineers work out how many computers they need by doing simple multiplication with guesses they write down, then they find ways to do the same work with fewer computers.

Think of it like this

Imagine a pizza company that delivers to a whole city. Its kitchen is enormous, with hundreds of specialist cooks: one only makes dough, one only does toppings, one only does sauces. Each order visits just a few of them, and runners carry half-made pizzas from station to station. If the stations are in one building, runners are quick. If half the stations are across the road, every pizza waits at the traffic lights. Now the owner asks two questions. How many ovens do we need for Friday at 8pm, when everyone orders at once? And how can we make each pizza 70% cheaper without it tasting worse? You answer both with guesses you write down: how many people order, how many pizzas each, how fast an oven bakes, what an oven costs per hour.

How it works

  1. Split the giant model. A mixture-of-experts model has hundreds of small "expert" networks. Each GPU holds some of them, and each word is sent to the few experts it needs.
  2. Keep the runners close. Sending words between GPUs in the same server is fast. Between servers it's much slower, so you arrange the split to keep most trips inside one server.
  3. Count the customers. Users per day, times chats per user, times messages per chat, gives requests per day. The busiest hour is a few times the average.
  4. Turn requests into GPUs. Each request reads and writes a number of tokens. Divide by how many tokens one GPU handles per second.
  5. Turn GPUs into money, then look for the biggest savings: smaller numbers, reused work, smart routing.

Why robots care

A robot fleet that asks a cloud model for help pays for every token. Knowing the cost per question tells you whether a robot can afford to "ask the big brain" every second or only when it's stuck. The robot's own computer has a fixed amount of memory, so a huge expert model can't live on board at all.

Try it

Words to know

Mixture of experts (MoE)
A model made of many small expert networks, with a router picking a few for each token.
Expert parallelism
Putting different experts on different GPUs and sending each token to the GPUs that hold its experts.
All-to-all
A communication step where every GPU sends something to every other GPU.
QPS
Queries (requests) per second.
GPU-hour
One GPU rented for one hour, the basic unit of serving cost.

Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.

Problem 23 · Serve a large MoE model: choose tensor, pipeline and expert parallelism, and explain all-to-all overhead

What the interviewer is testing: whether you can size the model against real GPU memory, match each kind of parallelism to the link it needs, and estimate all-to-all cost for prefill and decode separately.

The concepts

Module 08 Stage 06 explains the three classic splits for training (distributed training). Serving adds expert parallelism and changes the priorities, because decode steps are tiny and latency-sensitive.

all-to-all bytes per token per MoE layer ≈ k × d_dispatch × (bytes_dispatch + bytes_combine)
  k            experts chosen per token
  d_dispatch   width of the vector sent to each expert
fraction that leaves the GPU ≈ (E − 1) / E   for EP degree E and even routing

time per MoE layer ≈ max(compute, communication)   if overlapped well
                   ≈ compute + communication        if not
step time is set by the busiest GPU, so load imbalance multiplies everything

The rule of thumb: prefill is bandwidth-bound (thousands of tokens per step, so the all-to-all moves gigabytes), and decode is latency-bound (a few hundred tokens per step, so messages are small but there are two collectives per layer per token on the critical path).

Worked answer: Kimi K3 on H200s

Use Kimi K3, the model studied in Module 05. From its technical report: 93 layers (1 dense, so 92 MoE layers), 2.78T total and 104.2B active parameters, hidden size 7,168, 896 routed experts per layer with 16 active plus 2 shared, routed experts working in a 3,584-wide latent space with an expert hidden size of 3,072, expert weights in MXFP4 and activations in MXFP8, other components at higher precision. GPU figures are from NVIDIA's product pages: H200 SXM has 141 GB of HBM3e at 4.8 TB/s and 900 GB/s NVLink; a DGX B200 has 1,440 GB across 8 GPUs; DGX systems use 400 Gb/s ConnectX-7 network ports, one per GPU.

Step 1: does it fit? MXFP4 stores 4-bit values in blocks of 32 that share one 8-bit scale: 4 + 8/32 = 4.25 bits per weight. Assume each routed expert is a gated MLP with three 3,584 × 3,072 matrices.

routed expert params  = 3 × 3,584 × 3,072          = 33.0 M
× 896 experts × 92 layers                          = 2.72 T   (matches most of 2.78 T)
expert weights        = 2.72 T × 4.25 / 8 bytes    ≈ 1.45 TB
everything else       ≈ 0.06 T at BF16 (2 B)       ≈ 0.11 TB   (assumption: BF16)
total weights                                      ≈ 1.56 TB

one 8 × H200 node:  8 × 141 GB = 1,128 GB   → does not fit
one DGX B200:       1,440 GB                → does not fit
two 8 × H200 nodes: 2,256 GB                → fits, ≈ 0.7 TB left for KV cache and activations

So the minimum serving unit is two nodes. Now, how to split across them?

Step 2: all-to-all volume. Dispatch sends the 3,584-wide latent to 16 experts in FP8 (1 byte); assume combine returns BF16 (2 bytes).

per token per MoE layer = 16 × 3,584 × (1 + 2) B = 172,032 B ≈ 172 KB
× 92 MoE layers                                 ≈ 15.8 MB per token
(an upper bound: no deduplication when two chosen experts share a GPU)

Step 3: compare two layouts for one 16,384-token prefill chunk. Compute per MoE layer ≈ 2 × active params × tokens ÷ layers = 2 × 104.2 B × 16,384 ÷ 93 ≈ 3.67 × 1013 FLOPs. NVIDIA lists H200 FP8 at 3,958 TFLOPS with sparsity, so about 1,979 TFLOPS dense. Assume 50% of that is achieved. Treat NVLink's 900 GB/s as 450 GB/s each way, and each 400 Gb/s port as 50 GB/s each way.

Layout A: EP16 across both nodesLayout B: PP2 × EP8 (one stage per node)
GPUs per MoE layer168
Compute per layer3.67e13 ÷ (16 × 990 TFLOPS) ≈ 2.3 ms3.67e13 ÷ (8 × 990 TFLOPS) ≈ 4.6 ms
Tokens held per GPU1,0242,048
All-to-all per GPU per layer1,024 × 172 KB = 176 MB: half (88 MB) crosses the network at 50 GB/s ≈ 1.8 ms2,048 × 172 KB × 7/8 = 308 MB, all on NVLink at 450 GB/s ≈ 0.7 ms
Comms ÷ compute≈ 76%: needs near-perfect overlap≈ 15%: easy to hide
Extra costnonestage boundary: 16,384 × 7,168 × 2 B ≈ 235 MB per chunk over 8 ports ≈ 0.6 ms, once per chunk, not per layer
Decode2 × 92 = 184 collectives per token, all crossing nodes184 collectives per token on NVLink, plus one network hop
Memory per nodehalf of every layer's expertsall experts of half the layers ≈ 0.78 TB, ≈ 0.35 TB left

For decode, the bytes are small: 256 tokens per step gives about 4.8 MB per GPU per layer, around 10 µs on NVLink. What hurts is the fixed cost of each collective. Assume (to be measured with your own stack) about 15–30 µs per all-to-all inside a node and 50–100 µs across nodes at decode sizes. Then 184 collectives cost about 2.8–5.5 ms per token in Layout B and 9–18 ms in Layout A, against a TPOT target of perhaps 30–50 ms. That's why I'd pick Layout B for a two-node deployment: keep the all-to-all inside the NVLink domain, and put pipeline stages on the slower link where traffic is once per chunk.

Production systems push further: separate layouts for prefill and decode (disaggregation, page 04), routing limited to a few nodes per token, communication libraries such as DeepEP with a low-latency mode for decode, overlapping two micro-batches so one computes while the other communicates, and extra replicas of hot experts. The K3 report also describes a custom MoE decode kernel for small batches, where routed experts become memory-bound weight streaming, and overlapping the latent projections' communication with shared-expert compute (Module 05 · Serving).

Load imbalance. Each MoE layer waits for its busiest GPU. If the hottest GPU gets 1.5× the average number of tokens, that layer runs at two-thirds of its ideal speed. Balanced routing from training (K3's Quantile Balancing, in Module 05) and redundant copies of hot experts at serving time both attack this.

Trade-offs

SplitCommunicationLink it needsGood forCost
TP2 all-reduces per layerNVLinkAttention, large dense layersInefficient on small experts; duplicates MLA cache
PP1 send per stage boundaryNetwork is fineSpanning nodes cheaplyBubbles; needs several batches in flight; no per-token speed-up
EP2 all-to-alls per MoE layerNVLink ideallyHundreds of expertsLatency per layer; load imbalance
DP attentionNone for attentionn/aMLA-style small cachesUneven requests per GPU

How to say it in the interview

I start with memory: Kimi K3's 2.72T routed-expert parameters at MXFP4's 4.25 bits are about 1.45 TB, plus about 0.1 TB of higher-precision parts, so it doesn't fit one 8-GPU H200 node (1,128 GB) and needs two. Experts go on expert parallelism, because tensor-parallel slicing of small experts is inefficient, and attention runs data-parallel because MLA's cache is shared across heads. The all-to-all is about 172 KB per token per layer, so I keep it on NVLink: pipeline-parallel across the two nodes, EP8 within each. For a 16K-token prefill chunk that puts communication at about 15% of compute instead of about 76% with EP16 across the network. In decode the bytes are tiny but there are 184 collectives per token, so per-collective latency, overlap and load balance dominate; I'd measure them first.

Common mistakes

Problem 24 · Reduce cost per million tokens by 70% without hurting quality

What the interviewer is testing: whether you can write down the cost equation, see that 70% means a 3.3× improvement, and reach it with several measured levers, each guarded by a quality check.

The concepts

cost per 1M tokens = ($ per GPU-hour) / (tokens per second per GPU × 3,600) × 1,000,000

a 70% cut means cost × 0.3  →  tokens per GPU-dollar × 1 / 0.3 = 3.33×
independent levers multiply:   total = m1 × m2 × m3 × …

There are only three knobs: cheaper GPU-hours, more tokens per GPU-hour, or fewer GPU-hours spent on work that isn't needed. Every lever falls into one of them:

Worked answer with illustrative numbers

Baseline assumptions: $3 per GPU-hour, 1,500 output tokens per second per GPU at the latency target.

baseline = 3 / (1,500 × 3,600) × 1,000,000 = $0.556 per 1M output tokens
target   = 0.556 × 0.3                     = $0.167

The multipliers below are assumptions for illustration. The real ones come from benchmarking your workload, and they don't compose perfectly (bigger batches from FP8 shrink the speculative-decoding gain, for example).

LeverAssumed gainRunning totalQuality guard
BF16 → FP8 weights and FP8 KV cache1.6×1.6×Eval suite plus logit agreement vs BF16
Prefix caching with cache-aware routing (prefill saved on long chats)1.25×2.0×None needed: outputs are identical
Speculative decoding at moderate batch1.3×2.6×None needed if verification is exact
Route the easiest 50% of requests to a model 5× cheaper: cost 0.5 × 1 + 0.5 × 0.2 = 0.61.67×4.3×Router accuracy; non-inferiority test on routed traffic
1.6 × 1.25 × 1.3          = 2.6×   → cost × 0.385 (−62%): not enough alone
… × 1.67 (routing)         = 4.3×   → cost × 0.23  (−77%)
or … × 1.36 ($3 → $2.20 committed pricing) = 3.5× → cost × 0.28 (−72%)

"Without hurting quality" needs a definition you can test: a fixed eval suite with thresholds, plus a non-inferiority test online (the new system is no worse than the old by more than a small margin), using the canary process from Problem 19. The lossless levers (prefix caching, exact speculative decoding, batching, pricing) need no quality argument at all, so take them first.

Trade-offs

LeverQuality riskLatency effectEffort
QuantisationSome; must measureUsually betterLow to medium
Prefix cachingNoneBetter TTFTLow; routing is the hard part
Speculative decodingNone if exactBetter TPOT at low batchMedium; needs a draft
Model routing / cascadeReal; the router can misjudgeBetter for routed requestsHigh
Bigger batchesNoneWorse TPOT past a pointLow
Pricing / autoscalingNoneRisk of cold-start queuesContracts and ops work

How to say it in the interview

Cost per million tokens is dollars per GPU-hour divided by tokens per GPU-hour, so a 70% cut means 3.3× more tokens per dollar, and I'd get it by stacking levers rather than hoping one does it all. I'd take the lossless ones first: fuller continuous batches, prefix caching with cache-aware routing, exact speculative decoding, and committed pricing with autoscaling. Then FP8 weights and KV cache, gated by evals and logit agreement. If that's still short, route easy traffic to a distilled model, which carries the most quality risk and gets a non-inferiority canary. I'd measure each lever's real multiplier on our traffic, because they interact: bigger batches shrink the speculative gain.

Common mistakes

Problem 25 · Design ChatGPT-style inference for 1M daily active users

What the interviewer is testing: structured estimation (requests, peak, tokens, GPUs, dollars), awareness that every number is an assumption, and a sensible serving architecture around the estimate.

The concepts

First, clarify. Which model size? What latency targets (say, p95 TTFT under 1 s and TPOT under 50 ms)? How long are conversations? Free and paid tiers? One region or several? Then estimate in a chain, stating each assumption:

requests/day   = DAU × sessions per user × requests per session
average QPS    = requests/day / 86,400
peak QPS       = average QPS × peak factor
peak tokens/s  = peak QPS × tokens in   (prefill)
               + peak QPS × tokens out  (decode)
GPUs at peak   = (decode tok/s ÷ decode tok/s per GPU
               +  prefill tok/s ÷ prefill tok/s per GPU) ÷ target utilisation
cost/day       = GPU-hours/day × $ per GPU-hour
   provisioned for peak:  GPUs at peak × 24
   autoscaled (ideal):    Σ over hours of GPUs needed that hour

Cross-check the GPU count with Little's law: concurrent streams = arrival rate × time each request is in the system. That gives the KV-cache memory you need, which must fit next to the weights.

Worked answer (every number is an assumption)

AssumptionValueWhy this value
Daily active users1,000,000Given
Sessions per user per day3Guess; check against product analytics
Requests per session5Guess
Input tokens per request1,500System prompt plus chat history plus message; history grows within a session
Output tokens per request400Guess; reasoning models can be 10× this
Peak-to-average factor3One region with an evening peak; global traffic is flatter
Model and hardwareDense ~70B in FP8 on H100/H200-class GPUsA plausible mid-size chat model
Decode throughput per GPU1,500 output tok/sAssumption at a ~30 ms TPOT target; benchmark it
Prefill throughput per GPU15,000 input tok/sAssumption; benchmark it
Target utilisation70%Headroom for bursts and imbalance
Price per GPU-hour$2–$4Assumed range; varies widely by provider, commitment and GPU type
requests/day  = 1,000,000 × 3 × 5                 = 15,000,000
average QPS   = 15,000,000 / 86,400               = 173.6
peak QPS      = 173.6 × 3                          = 520.8

peak prefill  = 520.8 × 1,500                      = 781,250 tok/s
peak decode   = 520.8 × 400                        = 208,333 tok/s

GPU time      = 208,333 / 1,500  = 138.9   (decode)
              + 781,250 / 15,000 =  52.1   (prefill)
              = 191.0 GPUs at 100%
at 70%        = 191.0 / 0.7 = 272.8      → 273 GPUs at peak

provisioned for peak: 273 × 24        = 6,552 GPU-hours/day
autoscaled (ideal, daily curve below) = 2,196 GPU-hours/day

cost/day   low  = 2,196 × $2 =  $4,392    (autoscaled, cheap GPUs)
           high = 6,552 × $4 = $26,208    (peak-provisioned, expensive GPUs)
cost/month (30 days)          ≈ $132k to $786k
per DAU per month             ≈ $0.13 to $0.79
per 1M tokens (in + out)      ≈ $0.15 to $0.92   (28.5 B tokens/day)

Little's law cross-check. Each request is in the system for about 0.5 s of queue and prefill plus 400 tokens × 30 ms ≈ 12.5 s. Concurrent streams at peak ≈ 520.8 × 12.5 ≈ 6,500. With the Llama-70B attention shape (80 layers, 8 KV heads, head dim 128) and an FP8 cache, each token costs 2 × 80 × 8 × 128 × 1 B = 160 KiB. At about 1,900 tokens per stream that's about 297 MiB per stream and about 1.9 TB in total, roughly 7 GB per GPU across 273 GPUs. That fits easily next to 70 GB of FP8 weights on an H200 (141 GB), or on an H100 (80 GB) using tensor parallelism of 2. It also means about 24 streams per GPU at peak, comfortably below the roughly 45 that 1,500 tok/s at 33 tok/s per stream implies, so the throughput assumption isn't contradicted.

The range matters more than the midpoint. The six-fold spread between $4.4k and $26.2k a day comes from two choices you control (autoscaling, pricing) and one you mostly don't (the peak factor). Real autoscaling can't follow the curve perfectly, because of cold starts (Problem 20), so expect to land between the two bounds. The single biggest uncertainty is usually output length: a reasoning model producing 4,000 tokens instead of 400 multiplies decode GPUs by ten.

The architecture around the numbers:

  1. Edge and API gateway: authentication, per-user and per-tier rate limits, request size limits.
  2. Router: session-sticky and cache-aware, so a conversation's prefix stays on one replica (page 04). Picks a model pool by tier or difficulty.
  3. Model pools: replicas of vLLM, SGLang or TensorRT-LLM with continuous batching, chunked prefill, prefix caching and FP8 KV cache (page 08). Optionally disaggregated prefill and decode once traffic justifies it.
  4. Streaming: tokens over SSE; cancel generation when the client disconnects (page 01).
  5. Admission control and priority queues by tier, with graceful degradation under overload (page 03).
  6. Autoscaling on queue depth and KV utilisation, with a predictive schedule for the daily curve (page 09).
  7. Observability: TTFT, TPOT, queue depth, KV utilisation, prefix-hit rate, and cost per request, per tier.
  8. Two or more regions for availability; this also flattens the peak factor if users span time zones.

Interactive: capacity and cost estimator

Bars: GPUs needed in each hour of the day if autoscaling followed demand perfectly (assumed shape: one evening peak at 20:00, average 1, peak equal to the peak factor). Dashed line: GPUs if you provision for the peak all day. Every slider is an assumption; the cost is shown as a range from autoscaled at the low price to peak-provisioned at the high price.

Peak QPS
–
GPUs at peak
–
GPU-hours/day (auto–peak)
–
Cost per month
–
Per DAU per month
–
Per 1M tokens
–

–

Try this: the defaults reproduce the worked answer: 520.8 peak QPS, 273 GPUs, 2,196–6,552 GPU-hours a day, about $132k–$786k a month. Set output tokens to 4,000 (a reasoning model) and GPUs at peak jump to about 2,060. Set the peak factor to 1.5 (global traffic): peak GPUs halve to 137, and provisioned GPU-hours fall from 3× the autoscaled figure to 1.5×. If the low price is set above the high one, the two are swapped.

Trade-offs

DecisionCheaper optionSafer option
CapacityAutoscale tightly to the curveProvision near peak, absorb surprises
ModelSmaller or routed model for most trafficOne large model for everything
Utilisation target85–90%60–70%, for p99 latency headroom
RegionsOne regionTwo or more, active-active
PricingCommitted or reserved capacityOn-demand flexibility

How to say it in the interview

I'll state every assumption so we can change any of them. One million DAU at 3 sessions of 5 requests is 15 million requests a day, about 174 QPS on average and about 520 at a 3× peak. At 1,500 input and 400 output tokens, the peak is about 780K prefill and 208K decode tokens a second. Assuming a 70B FP8 model at 1,500 decode and 15,000 prefill tokens a second per GPU and 70% utilisation, that's about 273 GPUs at peak, and Little's law says about 6,500 concurrent streams needing roughly 7 GB of KV cache per GPU, which fits. At an assumed $2–$4 per GPU-hour that's about $4.4K a day autoscaled at the low price, up to $26K provisioned for peak at the high price, or $0.13–$0.79 per user per month. Output length and the peak factor move the answer most, so those are what I'd measure first.

Common mistakes

Practise it

  1. Replace the assumptions with measurements. On one GPU (Colab or rented), serve a small instruct model with vLLM and run its benchmark tool at several concurrency levels with 1,500-token inputs and 400-token outputs. Record output tok/s per GPU at the concurrency where TPOT stays under your target, and prefill tok/s from a prefill-only run. Put your numbers into the calculator and scale them by model size with care: say what you assumed.
  2. Build the cost waterfall. On the same setup, measure tokens per second for: baseline BF16, FP8 weights (if your GPU supports FP8; otherwise a 4-bit checkpoint), prefix caching on a workload with a shared 1,000-token system prompt, and speculative decoding. Compute each lever's real multiplier and cost per 1M tokens. Check whether they multiply or interfere.
  3. MoE all-to-all on paper, then in code. Recompute Problem 23's table for DeepSeek-V3 or another open MoE using its published config (layers, hidden size, experts, top-k). Then, on any machine with two or more GPUs, run nccl-tests' all-to-all benchmark at message sizes from 64 KB to 256 MB and find where it switches from latency-bound to bandwidth-bound. That crossover tells you which side of the line your decode and prefill steps sit on.

In this course

Why it matters for robotics

Self-check

Why can't Kimi K3 be served from one 8 × H200 node?

Its weights are about 1.56 TB even with experts in MXFP4 (about 1.45 TB of experts plus about 0.1 TB of higher-precision parts). One node has 8 × 141 GB = 1,128 GB. All parameters must be resident, not just the 104B active ones.

Why is MoE prefill bandwidth-bound and decode latency-bound in the all-to-all?

Prefill moves thousands of tokens per step, so each all-to-all carries hundreds of megabytes. Decode moves a few hundred tokens, so messages are small, but there are two collectives per MoE layer per token on the critical path, and each has a fixed latency.

Why put pipeline stages across nodes and expert parallelism within a node?

PP sends hidden states once per stage boundary, which slower network links handle. EP needs two all-to-alls per layer, which need NVLink-class bandwidth and latency.

Current cost is $0.60 per 1M tokens. What throughput multiplier gives a 70% cut?

Target $0.18 = 0.3 × 0.60, so you need 1 / 0.3 ≈ 3.33× more tokens per dollar.

2M DAU, 2 sessions, 4 requests per session, peak factor 4. Peak QPS?

16M requests/day ÷ 86,400 ≈ 185 average QPS; × 4 ≈ 741 peak QPS.

Why give the capstone cost as a range rather than one number?

Every input is an assumption: throughput per GPU, price per GPU-hour, peak factor, tokens per request, and how well autoscaling follows demand. The defaults here already span about 6× between autoscaled at a low price and peak-provisioned at a high price.

Resources