Skip to content

Add blog post: Running a big LLM across multiple GPUs with vLLM - #265

Draft
saiyam1814 wants to merge 6 commits into
mainfrom
blog/two-gpu-vllm
Draft

Add blog post: Running a big LLM across multiple GPUs with vLLM#265
saiyam1814 wants to merge 6 commits into
mainfrom
blog/two-gpu-vllm

Conversation

@saiyam1814

@saiyam1814 saiyam1814 commented Aug 18, 2026

Copy link
Copy Markdown
Member

What this is

A new blog post answering a reader question: someone with two A40s (45 GiB usable each) wanted to serve a BF16 model split across both cards, and asked how the weight splitting actually works.

The post covers:

  • The memory math that tells you whether a model fits before you download 60 GB
  • What tensor parallelism does to each weight matrix, column-parallel then row-parallel, and why that costs exactly two all-reduces per layer
  • The exact vLLM commands with real terminal output
  • Measured TP vs PP on a pair of cards with no NVLink
  • A40-specific catches, including the FP8 compute-capability limit

Where the numbers come from

Everything measured on two RTX PRO 6000 Blackwell cards held to a 40.47 GiB per-card budget (--gpu-memory-utilization 0.426) so the memory behaviour matches a 45 GiB A40 running at 0.90. Model is Qwen3-32B in BF16, a 61.02 GiB checkpoint, on vLLM 0.27.1.

Headline results:

TP=2 PP=2
tok/s at concurrency 1 36.41 21.00
tok/s at concurrency 32 496.60 487.56
median TTFT at 32 3892 ms 2468 ms
KV cache 67,296 tokens 56,640 tokens

Worth flagging for reviewers: vLLM's docs recommend pipeline parallelism over tensor parallelism on boxes without NVLink for "higher throughput", and that did not reproduce here. The post reports it as a miss rather than bending the result, and explains why the crossover may sit differently on Gen4 A40s.

New animations

Three CSS-only animations following the existing series pattern (no hooks, no use client, theme variables, prefers-reduced-motion guards), registered in lib/markdown.js:

  • {{two-gpu-tensor-split-animation}} the split and the all-reduce
  • {{two-gpu-memory-fit-animation}} 61.02 GiB against one card, then two
  • {{two-gpu-tp-vs-pp-animation}} the tradeoff plus measured results

Verified rendering in both light and dark mode.

Notes for review

  • draft: true had to be removed so the post renders on the Cloudflare preview: drafts are
    filtered out of production builds (INCLUDE_DRAFTS is false when NODE_ENV is production), so
    the preview 404s while the flag is set. This PR stays in draft as the gate, so nothing
    publishes until it is marked ready and merged. Re-add the flag if you would rather hold it after merge.
  • The TP-vs-PP animation repeats four numbers that also appear in the prose table just below it. Happy to drop either.
  • Cover is generated by scripts/gen-two-gpu-vllm-cover.mjs and converted with rsvg-convert -w 1200 -h 630.

Answers a reader question about splitting a BF16 model across two A40s:
how tensor parallelism partitions each layer, the memory math for whether
it fits, and measured TP vs PP numbers on a pair of cards with no NVLink.

All figures come from a real run (vLLM 0.27.1, Qwen3-32B BF16) on two
RTX PRO 6000 cards held to a 40.47 GiB per-card budget to match a 45 GiB
A40 at --gpu-memory-utilization 0.90.

Adds three CSS animations following the existing series pattern:
- two-gpu-tensor-split-animation: column/row-parallel split, one all-reduce
- two-gpu-memory-fit-animation: 61.02 GiB against one card, then two
- two-gpu-tp-vs-pp-animation: the TP/PP tradeoff plus measured results

Marked draft: true pending review.

Signed-off-by: Saiyam Pathak <saiyam911@gmail.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 18, 2026

Copy link
Copy Markdown

Deploying website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2eb05a8
Status: ✅  Deploy successful!
Preview URL: https://26e84689.website-dab.pages.dev
Branch Preview URL: https://blog-two-gpu-vllm.website-dab.pages.dev

View logs

Drafts are filtered out of production builds (INCLUDE_DRAFTS is false when
NODE_ENV is production), so the preview deploy 404s on the post while the
flag is set. Removing it makes the PR preview reviewable. The PR itself
stays in draft, so nothing publishes until it is marked ready and merged.

Signed-off-by: Saiyam Pathak <saiyam911@gmail.com>
Replaces the two-GPU A40 post. The old version simulated a 2-GPU split by
capping memory on larger cards, which meant the central claim was emulated
rather than measured. This version uses a model that genuinely does not fit:
Qwen3-235B-A22B-Instruct-2507-FP8, 221 GiB on disk, 2.3x too big for one
96GB card.

Reframed away from A40 specifics to the general question of how a big model
is spread over several GPUs and how inference works once it is. Byline is now
Shubham and Saiyam. Rewritten in plain English throughout, with every flag
and every line of the docker command explained.

All three splitting modes measured on the same 4 GPUs:
- TP=4:      17.14 ms/token, 503.68 tok/s at 32 concurrent, 621,392 KV tokens
- TP=4 + EP: 18.83 ms/token, 470.93 tok/s, 623,696 KV tokens
- PP=4:      21.19 ms/token, 296.48 tok/s, 555,680 KV tokens, best TTFT

Plus three real failure modes with their actual error text: an invalid
tensor-parallel size, a genuine CUDA OOM at 2 GPUs, and a DeepGEMM
"Unknown SF transformation" crash on sm_120 that needs VLLM_USE_DEEP_GEMM=0.

Adds Part 1 on downloading and on-disk storage (safetensors shards, the HF
cache blob layout, FP8 block scales) and Part 3 on what inference actually
does (prefill versus decode, continuous batching, why capacity is set by the
KV cache). Includes the disk-pressure hazard that evicted pods on our own
test node.

Four CSS animations replace the previous three, following the existing
series pattern: three-ways-to-split, tensor split across 4 GPUs, expert
routing, and memory fit on 1 / 2 / 4 GPUs.

Signed-off-by: Saiyam Pathak <saiyam911@gmail.com>
@saiyam1814 saiyam1814 changed the title Add blog post: Running a single LLM across two GPUs with vLLM Add blog post: Running a big LLM across multiple GPUs with vLLM Aug 18, 2026
saiyam1814 and others added 3 commits August 18, 2026 17:15
A reader asked whether the shard count can be changed at download time and
whether there is a standard. Adds two subsections to Part 1: shards are fixed
by the publisher and recorded in model.safetensors.index.json, re-sharding is
a local save_pretrained(max_shard_size=...) operation, and the Hub's <200GB
recommendation plus 500GB hard limit explain why publishers land around
5-10GB. This model uses a 10GB cap: 23 shards of exactly 10.00 GB plus a
6.45 GB remainder. Also notes that shard count does not affect serving,
because safetensors are memory-mapped.

Signed-off-by: Saiyam Pathak <saiyam911@gmail.com>
Signed-off-by: Saiyam Pathak <saiyam911@gmail.com>
…tion-focused and theory-focused readers each get a direct path through it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants