vllm-project

Stars and mentions

Last 30 days, Oct 4 – Nov 2

GitHub stars2,799
mentions198
04896144192Oct 4Oct 18Nov 2
UTC days · repositories vllm-project owns11 repositories

Package downloads

Cumulative recorded downloads · since 2026-04-08

crates.io—
PyPI43.2M
010.8M21.6M32.4M43.2MOct 4Oct 18Nov 2
Nov 1crates.io: 0/1 packages observed · PyPI: 1/14 packages observed

Across 15 known packages in repositories vllm-project owns. Cumulative totals include recorded downloads before the selected range. Missing observations leave gaps; totals may be incomplete. PyPI excludes known mirrors.

Top repositories

vllm-project’s repositories ranked by pull request authors, as on trending.

Loading

Contributes to

Repositories outside vllm-project’s account where vllm-project opened or reviewed pull requests in the past 365 days.

Who noticed vllm-project

Everyone who starred or forked vllm-project’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

202posts
Nov 36 posts
  1. vLLM Fast Start Explained: What Preload and Snapshots Save, What They Cannot, and the Fingerprint Gap TL;DR - What shipped: vLLM 0.31.0 (2026-10-05) added vllm preload, a per-GPU daemon that keeps post-quantized weights resident so a restarting engine maps them over CUDA IPC instead of reloading from disk, plus experimental CRIU-based initialized-engine snapshots. The design goal, in the original PR's words, is that "engine restarts are dominated by weight loading from disk." - What it does not do: the daemon is node-local and must itself load from disk once, so it speeds up restarts on a GPU that already holds the weights. It does not speed up scaling out to a fresh node. - What a faster start is worth (illustrative simulation): with a 1.5x traffic burst and a 10 s queue-wait SLO, cutting replica startup from 300 s to 45 s raises the safe baseline utilization from 69% to 83%. For 100 GPUs of baseline load that is 145 GPUs versus 120. At 2x bursts it is 196 versus 159. - A correctness gap to know about: as of 2026-11-03, issue #59647 is still open. The weight-cache fingerprint hashes safetensors headers, not weight values, so a daemon holding checkpoint A can serve an engine started on checkpoint B without any warning. The proposed fix samples only 1 KiB per tensor, which on Qwen3-8B is 0.002% of the file. What did vLLM actually ship for fast restarts? The feature landed in two pieces. The first is the weight cache daemon, merged as PR #54921 on 2026-09-04 and exposed as vllm preload in v0.31.0. One daemon process per GPU loads its tensor-parallel shard, runs quantization post-processing, and exports every parameter as a CUDA IPC handle. An engine started with --load-format ipc_cache builds the model on the meta device and asks the daemon for the tensors. The second piece is vllm snapshot create/restore, which uses CRIU to freeze a fully initialized engine and bring it back before the HTTP server binds. # one daemon per GPU, loads from disk once vllm preload --model meta-llama/Llama-3.1-8B-Instruct --tensor-parallel-size 4 # engine restarts map the resident weights instead of reading disk vllm serve meta-llama/Llama-3.1-8B-Instruct \ --tensor-parallel-size 4 --load-format ipc_cache The two mechanisms keep very different amounts of state, and their constraints differ accordingly. Everything in this table comes from the vLLM docs pages for the two features as of today. | | Weight preload | Initialized snapshot | |---|---|---| | What persists | Post-quantized weights in GPU memory (and FlashInfer autotune results on disk) | The whole engine process tree and CUDA state | | Scope | Same node, same user, Unix socket | Same host, GPU, driver, kernel, vLLM version, engine args | | Parallelism | Tensor, expert and data parallel, including multi-node (one launcher per node) | TP1, one NVIDIA GPU only | | Validated | PR test results include TP2 and MTP draft models | Only dense float16 TP1; acceptance run was Qwen3-0.6B on an H200 | | Cost while idle | The daemon pins the weights in GPU memory | Disk: artifact size can approach process plus GPU memory | | Fallback | Disk load if daemon missing or fingerprint mismatched | None: restore fails closed before CRIU runs | One number from outside vLLM gives a sense of scale for the weight-load part alone. The README of a separate PyPI plugin built on the same idea, vllm-ipc-cache 0.2.0, reports a 27B model at tensor parallel 4 on a MetaX N300-A dropping from 35.80 s to 0.47 s. It is one vendor-reported run on non-NVIDIA hardware and covers the weight-attach step only, not an end-to-end restart. Which startup events does a faster restart actually help? Preload covers only some of the reasons you start an engine. 1. Rolling upgrades and config changes. The daemon outlives the engine, so you restart the engine and keep the weights. This is the cleanest win. 2. Engine crashes where the GPU is healthy. The daemon is a separate process, so a crashed engine can remap the same weights. If the fault is at the GPU level, the daemon probably goes down with it and you are back to a disk load. I did not test that case. The docs say that if any daemon rank exits, you should restart the whole preload group before attaching engines. 3. Scale-out to a new node. Not helped. CUDA IPC handles are node-local, the docs state that each node serves only its own GPUs, and the daemon itself loads from disk. The one scale-out feature is warmup-cache seeding: a new daemon can fetch rank-matched FlashInfer autotune tables from a source replica over an authenticated, unencrypted TCP channel. 4. Time-sharing a GPU between models. This is what snapshots are for: "repeatedly activating the same model and engine configuration on the same machine." The autotune detail matters more than it looks. The docs say that once weights come from the daemon, the FlashInfer autotune pass "dominates what is left of the startup time" on a large MoE model, which is why vllm preload now runs it at preload time. v0.31.0 also parallelized Triton warmup compilation (#58582). So the honest accounting of a restart is weights, then autotune, then warmup and CUDA graph work, and each has its own fix. Here is the arithmetic that makes rolling upgrades the main prize. A rolling restart of 16 replicas, one at a time, runs the fleet at 15/16 capacity (93.75%) for 16 times the per-replica restart time. !Rolling restart of 16 replicas, minutes at 15/16 capacity, by per-replica restart time At 300 s per replica that is 80 minutes of reduced capacity; at 45 s it is 12 minutes; at 10 s it is 2.7 minutes. If you ship engine upgrades every couple of weeks, that is the number to compare against the cost of keeping a daemon pinned. How much utilization does a faster start buy you? Startup time matters to capacity planning through one mechanism: during a burst, demand exceeds capacity for roughly the detection lag plus the startup time, and the backlog built in that window has to drain inside your queue-wait SLO. If the burst multiplies load by B, baseline utilization is u, the window is W seconds and the SLO is S seconds of queue wait, the backlog stays inside the SLO when: (B * u - 1) * W <= S so u_max ~= (1 + S / W) / B, W ~= startup + detection lag I checked this with a fluid-queue simulation (illustrative, not a measurement of any real fleet): 16 replicas, a 30-minute run, demand ramping by a factor B over 30 s, a scaler that averages demand over 30 s, checks every 10 s and restores the baseline utilization, and a violation counted for every second the queue wait exceeds 10 s. The core loop: for t in range(T_END): lam = demand(t, lam0, burst) n += sum(c for r, c in pending if r <= t) # replicas that finished starting pending = [(r, c) for r, c in pending if r > t] if t % 10 == 0 and t >= 30: want = ceil(mean(hist[-30:]) / (u0 * MU)) # restore baseline utilization if want > ordered: pending.append((t + startup, want - ordered)); ordered = want q = max(0.0, q + lam - n * MU) # fluid backlog violations += (q / (n * MU)) > 10.0 For each startup time I searched for the highest baseline utilization with zero violation seconds: | Replica startup | Max safe baseline utilization, burst x1.5 | GPUs for 100 GPUs of load | Max safe utilization, burst x2.0 | GPUs for 100 GPUs of load | |---|---|---|---|---| | 300 s | 0.69 | 145 | 0.51 | 196 | | 120 s | 0.73 | 137 | 0.54 | 185 | | 45 s | 0.83 | 120 | 0.63 | 159 | | 10 s | 0.99 (search cap) | 101 | 0.85 | 118 | The closed form with W set to startup plus 45 s predicts 0.686 for the 300 s row against 0.69 in the simulation. It is conservative for fast starts, because the ramp is finite and the scaler orders in steps; at 45 s it predicts 0.74 against 0.83. The 10 s row is the idealized limit, since it ignores KV cache warmup, load balancer registration and anything else that happens after the process is up. !GPUs needed to carry 100 GPUs of baseline load by replica startup time The same data as violation seconds shows how sharply the cliff moves. At 80% baseline utilization and a 1.5x burst, a 300 s startup spends 413 s over the SLO with a peak queue wait of 57 s. At 120 s it is 113 s over, and at 45 s it is zero violations with a peak wait of 7.6 s. !Seconds over a 10 s queue-wait SLO during a x1.5 burst, by baseline utilization and startup time Two readings follow. First, going from 300 s to 45 s buys about 17% to 19% fewer GPUs in these scenarios, which is real money but not a revolution. Second, those gains apply to scale-out, and preload does not accelerate scale-out to fresh nodes. Where this model really applies is a parked warm replica on the same host (a daemon on an idle GPU, or a snapshot on disk), and that parked GPU costs about the same as the headroom replica you were trying to avoid. For most fleets the dependable return is the rolling-restart arithmetic above, not the autoscaling story. Can the weight cache serve the wrong checkpoint? It can today, in a specific case, and the project knows. The fingerprint that decides whether a daemon's weights match the engine covers architecture, TP and DP layout, dtype, quantization, RoPE parameters, KV cache dtype and vLLM version. For the checkpoint itself it hashes each safetensors shard's header: tensor names, dtypes, shapes and byte offsets. A header contains no weight values. Issue #59647, filed 2026-10-01 and still open on 2026-11-03, reports that a base model and its fine-tune therefore get the same key. If a daemon preloaded from A is still running and an engine starts on B with ipc_cache, the daemon serves A's tensors, the engine never reads B from disk, and nothing is logged. A write-up on dev.to by a separate author reproduced it on 0.30.0 by zeroing one norm tensor while leaving the header intact. The docs promise a safe fallback on mismatch, which is why this surprises people. The open fix, PR #59648, adds file size plus the first 1 KiB of every tensor to the hash. I re-implemented both schemes (the logic is small) and ran them on eight 1024x1024 float16 tensors, then changed the values in different ways: | Change to the checkpoint | Header-only hash detects it | Sampled 1 KiB hash detects it | |---|---|---| | Noise added to every tensor (a dense fine-tune) | no | yes | | One whole tensor shifted by a constant | no | yes | | First row of one tensor zeroed | no | yes | | Row 500 of one tensor zeroed | no | no | | One element at row 900 changed | no | no | | Byte-identical copy in another directory | same key (wanted) | same key (wanted) | The proposal catches realistic fine-tunes, because training changes nearly every value, including each tensor's first kilobyte. It cannot catch a surgical edit that leaves the head of each tensor alone. The reason is coverage: on the real headers I fetched, Qwen3-0.6B has 311 tensors and 1.50 GB, so 1 KiB per tensor is 0.018% of the file; Qwen3-8B is 0.0022%; Qwen3-32B has 707 tensors over 65.5 GB and the sample is 0.00095%. !Bytes in the checkpoint versus bytes sampled by the proposed fingerprint, on Qwen3-0.6B, 8B and 32B That is a fine tradeoff for a startup-path check, since reading everything would defeat the purpose. The practical conclusion is not to rely on the fingerprint for identity. Tie the daemon's lifetime to a digest you computed once, at promotion time, with the full file bytes. SHA-256 ran at about 1.8 GB/s on my PC (median of five runs on an in-memory buffer), so a 65.5 GB checkpoint is roughly 36 s of CPU time if the disk can feed it, paid once in CI and not on every restart. # at promotion time, once per checkpoint DIGEST=$(sha256sum /models/qwen3-8b/*.safetensors | sha256sum | cut -c1-16) SOCK=/run/user/$UID/vllm-wc/$DIGEST mkdir -p -m 700 "$SOCK" vllm preload --model /models/qwen3-8b --weight-cache-socket-dir "$SOCK" vllm serve /models/qwen3-8b --load-format ipc_cache \ --model-loader-extra-config "{\"socket_dir\": \"$SOCK\"}" An engine pointed at a different checkpoint computes a different digest, looks in a different directory, finds no socket, and falls back to disk, which is the documented behavior for an unavailable daemon. I used only flags from the preload docs and did not run this against a GPU engine for this post. Add a temperature-0 canary request per checkpoint after every start; the snapshot feature does exactly that internally, comparing a recorded token and log probability before it returns. A pre-flight checklist for adopting fast start 1. Count your restart events by type for a month: upgrades, crashes, scale-outs. Preload only pays on the first two. 2. Measure your own restart phases separately: weight load, autotune, warmup and CUDA graphs. Do not assume weights are the whole story. 3. Key the daemon to a full-file digest, and run a canary after each start. 4. Keep the disk fallback enabled in production and alert on it. Budget the GPU memory the daemon pins, and do not combine the loader with sleep mode. FAQ What does vllm preload do? It starts one daemon per GPU that loads and post-processes the weights once and keeps them in GPU memory. Engines started with --load-format ipc_cache map those weights over CUDA IPC instead of reading the checkpoint again. Does preload speed up autoscaling to a new GPU node? No. The handles are node-local and the daemon must load from disk the first time. It helps restarts on a node that already holds the weights. Only the FlashInfer autotune tables can be seeded to a new daemon from another replica. Can the weight cache load the wrong model? Under the current fingerprint, yes, if two checkpoints share names, shapes, dtypes and offsets and a daemon for the other one is still running (issue #59647, open on 2026-11-03). A digest-keyed socket directory and a canary request avoid it. How much GPU capacity does faster startup save? In my illustrative simulation with 1.5x bursts and a 10 s queue-wait SLO, 45 s startup needs 120 GPUs where 300 s needs 145, for 100 GPUs of baseline load. Your own burst size, detection lag and SLO will move that. Further reading - Your LLM Serving Engine Ships Every 13 Days: Reading vLLM's Breaking-Change Lists as Upgrade Risk Data - What Does It Actually Cost to Self-Host an LLM? The Batching Math Nobody Shows You Sources: vLLM docs, Preload; vLLM docs, Initialized engine snapshots; vLLM v0.31.0 release notes; PR #54921 Fast Start; PR #51360 snapshots; Issue #59647; PR #59648; vllm-ipc-cache on PyPI; Qwen3-8B and Qwen3-32B safetensors headers.

  2. Reddit · · r/LocalLLaMA · u/rmhubbert · vllm-project/vllm

    vLLM now has PLE disk offload support on nightly

    169 points · 21 comments
  3. vLLM・SGLang・TensorRT-LLMをクラウドGPUで動かしたら、それぞれの個性が見えてきた この記事で分かること Qwen3-8Bを、L4 24GB 1台の上でvLLM・SGLang・TensorRT-LLMに載せ、同じリクエストを送った。揃えたつもりの条件が、5か所で揃っていなかった。どれもベンチマークの数字に入り込む。 速さの順位は載せない(速さは自分の条件で測ってほしい)。すべてこの版・L4(sm_89)での話である。エンジンを選びたいだけなら「エンジン別の要点」と最後の「私の使い分け」へ。 エンジン別の要点 エンジン 知っておくこと vLLM 0.31.0 L4ではQwen/Qwen3-8B-FP8が重みだけFP8(W8A16)で動く SGLang 0.5.21 ツール定義に項目が足され、入力が増える。L4・8B bf16は--mem-fraction-staticの自動値では起動しない(0.87で動いた) TensorRT-LLM 1.2.1 L4ではQwen/Qwen3-8B-AWQ・-FP8をそのまま読めない。KVが足りないと黙って待たせる。キャッシュを消すAPIが無い 用語 用語 意味 KVキャッシュ 生成中のリクエストが、それまでのトークンの計算結果を置いておくGPUメモリ。容量をトークン数で数え、同時に処理できる本数の上限を決める プリフィル 入力(プロンプト)をまとめて読み込む最初の段階。入力が長いほど重い TTFT Time To First Token。送ってから最初の1トークンが返るまでの時間 W8A16/W8A8 重み(Weight)と計算時の値(Activation)のビット数。W8A16は重みだけ8bitで計算は16bit 追い出し(preemption/retraction) KVが足りなくなったとき、処理中のリクエストをいったん外すこと。後でやり直す RadixAttention SGLangのプレフィックスキャッシュ。木構造で、任意のトークン位置まで一致を使い回す(vLLMは決まった長さのブロック単位) sm_89/Ada/Hopper NVIDIA GPUの世代。L4はAda(sm_89)、H100はHopper(sm_90) ModelOpt NVIDIAの量子化ツール。TensorRT-LLM向けの量子化モデルを作る 条件 項目 値 モデル Qwen3-8B(bf16、AWQ 4bit、FP8) GPU NVIDIA L4 24GB 1台(sm_89、AWS g6.xlarge)。ドライバ 595.112.07、CUDA 13.2 エンジン vLLM 0.31.0 / SGLang 0.5.21 / TensorRT-LLM 1.2.1(trtllm-serve、PyTorchの経路) リクエスト /v1/chat/completions。ツール定義10個(ケースごとに40個から選ぶ)+日本語の質問。システムプロンプトなし、thinkingオフ、質問の末尾にリクエストごとの識別子 max_tokens/最大の文脈長 512/8,192 測った並行数 AWQ・FP8は1と16。bf16は1(16は載らない:3節)。SGLang bf16は容量の確かめで8も 送ったケース 200ケースのうち20ケースを繰り返し送った(並行数1で80件) プレフィックスキャッシュ 3エンジンとも既定で有効。区切り(エンジン×チェックポイント×並行数×測定の種類)ごとにサーバーを起動し直す エンジン別 vLLM SGLang TensorRT-LLM 入力長 最大/中央値(200ケース) 2,591/2,310 2,691/2,410 2,591/2,310 ツールパーサー hermes qwen(Qwen25Detector) qwen3 バッチの上限 max_num_seqs 256・max_num_batched_tokens 2048 max_running_requests 2048 max_batch_size 2048・max_num_tokens 8192 チャンク化プリフィル 有効 有効(4096) 無効 CUDA graph FULL_AND_PIECEWISE 有効 バッチ1〜32・64・128 バッチの上限はどれも並行数16より大きい。 1. 入力:SGLangだけトークン数に差が出た 同じリクエストでusage.prompt_tokensを比べると、SGLangだけ全ケースで+100トークンだった。 // 送ったもの({…}は省略) {"type": "function", "function": {"name": "search_master", "description": "…", "parameters": {…}}} // テンプレートに渡るもの {"type": "function", "function": {"description": "…", "name": "search_master", "parameters": {…}, "strict": false}, "defer_loading": null} エンジン toolsの扱い prompt_tokens vLLM 型に通すが、テンプレートに渡す長さは変わらない 送ったまま手元でテンプレートに通した長さと一致 SGLang model_dump()で項目を足し、キーの順が変わる +100 TensorRT-LLM — vLLMと一致 書き換えを再現する関数を書き、1,200件でSGLang自身のmodel_dump()と突き合わせて違いは0件だった。確かめるなら、テンプレート適用後の文字列(vLLMなら/tokenize)の差分を取る。 切り替えるオプションは無い。 揃えるならクライアントから足した形で送る(他エンジンの入力も変わる)。今回はそのまま測った。 効き方:プリフィルが重くなる/KVの見積もりがずれる(3節)/精度にも混ざる。ツールが多いほど差は大きい。パーサーも3つとも別の実装で、読み損ねは精度の差として出る。 2. 量子化:「FP8」が同じ意味ではない エンジン 起動時の警告 vLLM Weight-only FP8 compression will be used leveraging the Marlin kernel. SGLang Using default W8A8 Block FP8 kernel config. Performance might be sub-optimal! 「FP8で比べた」表は、重みだけ8bitと計算も8bitが混ざりうる。起動ログのカーネル名で確かめる。 vLLMがL4でW8A16になるのは、カーネルの候補を順に試し、最初に使えるものを選ぶため(vllm/model_executor/kernels/linear/init.pyのinit_fp8_linear_kernel)。 CUTLASSのブロックFP8のカーネルはsm_90・sm_100・sm_120向けしか無い(csrc/.../c3x/scaled_mm_blockwise_sm*_fp8.cu)。--linear-backendで変えられるはずだが試していない。 TensorRT-LLM 1.2.1・L4では、公開の次の2つを読めなかった。 チェックポイント 読めない理由 代わり(ModelOpt 0.37.0) Qwen/Qwen3-8B-AWQ(AutoAWQ形式) load_hf_quant_configにawqの分岐が無い --qformat int4_awq Qwen/Qwen3-8B-FP8(ブロック単位) 対応表で、ブロック単位FP8はAda(sm_89)が対応外(HopperとBlackwell sm100/103は対応)。モデル側の表でもQwen3は対応外なので、H100でも使えない --qformat fp8 --kv_cache_qformat none 2つで約56分かかった。nvidia/Qwen3-8B-FP8はKVまでFP8になるため使わなかった(KVの型の上書きは試していない)。TensorRT-LLMだけチェックポイントが別物なので、量子化した構成はエンジン間で比べず、各エンジン内でbf16との精度差だけ確かめた。 3. 容量:同じGPUでも載る量が違う 名前の似たメモリ設定が、何に対する割合かが違う(L4は約22.5GiB〔23,034MiB〕、Qwen3-8B bf16の重みは15.27GiB)。 | weights | KV | act | rest | GPU |===============|==============|=========|======| vLLM |<---------------- 0.92 ---------------->| SGLang |<----------- 0.87 ----------->| TRT-LLM |<-------- 0.9 x free --------->| act=活性化など計算中の作業領域(プリフィルの中間結果、CUDA graph)。rest=エンジンの外で使われる分の余白。模式図で、長さは比率どおりではない。 エンジン 割合の対象 割合に含むもの 値 KVの容量(トークン) 容量を直接指定するなら vLLM GPU全体 重み+KV+活性化 0.92(既定) 30,240 --kv-cache-memory-bytes(指定すると0.92は無視される) SGLang GPU全体 重み+KV(活性化は外) 0.87(手で決めた) 26,813 --max-total-tokens TensorRT-LLM 重みと活性化を確保した後の空き KVだけ 0.9(既定) 約32,800 kv_cache_config.max_tokens(割合と両方なら小さいほう) 揃えたいなら割合ではなくトークン数で直接指定する(オプション名は版で変わりやすい)。今回は既定に近い状態を測るため揃えていない。 KV 1トークン = 36層 × KVヘッド8 × 128次元 × K/V 2 × 2バイト = 144KiB 1本の最大 = 入力2,691(SGLang) + max_tokens 512 + 余裕1 = 3,204 並行数8 = 3,204 × 8 = 25,632(+100前は 3,104 × 8 = 24,832) 並行数16 ≒ 51,300 → bf16は3エンジンとも載らない 全リクエストが最大長まで伸びても入るかで数え、入らない組み合わせは測っていない。 SGLangは自動値(0.684)で起動しなかった。 自動値=(GPUの容量−作業領域の見積もり)÷GPUの容量(srt/arg_groups/memory_hook.py)。 作業領域の見積もり(L4) 大きさ 固定の分 512MiB プリフィル chunked_prefill_size 4,096トークン×1.5MiB=6GiB(係数1.5はソースで「経験的な値」) 並列化の分 128MiB(1GPU) CUDA graph デコードの最大バッチ48×2MiB+プリフィルの分 合計 約7.1GiB(23,034MiB×(1−0.684)) ValueError: Loaded weights leave no GPU memory for the KV cache under --mem-fraction-static=0.684. 並行数8が載る値を0.01刻みで探した(0.01あたり約1,590トークン)。 割合 KVの容量 並行数8(25,632) KV以外の残り 追い出し 0.684(自動) 起動しない — — — 0.84 22,047 載らない 1.82 GB — 0.86 25,224 載らない 1.40 GB — 0.87 26,813 載る 1.11 GB 0回 +100前なら0.86で足りたが、1節の+100で408トークン足りなくなり0.87にした。0.88以上は試していない。 4. 溢れたとき:追い出すか、黙って待たせるか 方式 エンジン 混んだとき遅れる場所 見える指標 追い出す vLLM・SGLang 生成の途中(ストリーミングが止まる) vllm:num_preemptions/sglang:num_retracted_requests_total 待たせる TensorRT-LLM 最初の1トークン(TTFT) 無い。numActiveRequestsで見る TensorRT-LLMは「入力長+max_tokens」ぶんを先に確保するので、max_tokensを大きく付けるほど同時に受け入れる本数が減る。並行数16で送っても中では8本しか走っていないことがある。空きに合わせて詰め込むMAX_UTILIZATIONもある(処理中のリクエストが一時停止しうる。試していない)。 今回は/metricsを2秒ごとに読み、numActiveRequestsの最大が送った本数に届いたことを確かめた。溢れる組み合わせは測っていない(3節)。 5. 観測:そのまま読むと誤判定になる エンジン 癖 対処 SGLang キャッシュヒットが0だとprompt_tokens_detailsごと出ない 無ければ0 SGLang num_retracted_requests_totalは初回の追い出しまで出ない ゲージnum_retracted_reqsがあれば0回 TensorRT-LLM kvCacheStatsはenable_iter_perf_statsが要り、1件処理するまで空 短いリクエストを1件送ってから読む TensorRT-LLM その1件で、測定の最初のリクエストがcached_tokens=1になった(<|im_start|>を共有。partial reuseが既定で有効なので、32トークンのブロックの一部でも当たる) 書式を通さない/v1/completionsで送る TensorRT-LLM 反復ごとの統計は読むたびに消える 測定中に2秒ごとに読む 「読めなければ0」にしていたら誤った結論を出していた。「読めなければ止める」作りにしていたので気づけた。 エンジン キャッシュの消し方 vLLM POST /reset_prefix_cache(VLLM_SERVER_DEV_MODE=1) SGLang POST /flush_cache TensorRT-LLM APIなし。再起動 キャッシュがどれだけ効くかは、送るケースの数とKVの容量で大きく変わる。今回は20ケース(入力の合計45,957トークン)を繰り返し送った。 チェックポイント KVの容量(トークン) 20ケースぶんが cached_tokens/入力(3エンジンとも同程度) bf16 26,813〜32,800 収まらず、一周する前に追い出される 3.6〜4.7%(当たるのは先頭の書式 約50トークンだけ) AWQ・FP8 76,096〜99,552 収まり、2周目からツール定義ごと当たる 並行数1で約75%、16で約82% 効いているのは並行数ではなく、量子化で空いたKVの容量である。量子化した構成の速さには、計算が軽くなった分とキャッシュに当たった分が混ざりうる。ケースの数・繰り返し方とKVの容量を並べて確かめたい。 6. 動かすまで 版がぶつかるので、仮想環境をエンジンごとに分け、クライアントはHTTPだけで話す。 エンジン torch transformers vLLM 2.13.0+cu132 5.17.0 SGLang 2.13.0 5.12.1 TensorRT-LLM 2.9.1+cu130 4.57.3 問題 対処 TensorRT-LLM環境でModelOptが動かない(NumPy<2固定と最新pyarrowが衝突) pyarrow<22 TensorRT-LLM FP8が起動中にsegfault(原因不明、再実行で通過) — そのとき親プロセスが残り、20分待ち続けた ログにSegfault encountered等が出たら即失敗にして1回だけ再起動 まとめ:比較を作る・読むときのチェックリスト 確かめること 確かめ方 モデルに届く入力は同じか usage.prompt_tokensを並べる。テンプレート適用後の文字列を比べる ツールパーサーは何か 起動オプションを書く 量子化の計算は何か 起動ログのカーネル名・警告 量子化のチェックポイントは同じか 対応表とソースで、使うGPUで読める形式か確かめる KVの容量はいくつか 割合ではなくトークン数で見る。揃えるなら直接指定する 溢れたら何が起きるか 追い出しカウンタと同時実行数。max_tokensも書く 指標が読めないときの扱い 読めなければ止める キャッシュの状態 有効か、区切りごとに消すか、cached_tokensはいくつか。ケースの数・繰り返し方とKVの容量も書く 私の使い分け(推奨案) 速さではなく、導入の手間・設定の素直さ・観測しやすさで決めた。 当てはまる範囲:24GBクラスのGPU1台・8B級・同時8本程度まで。大きいGPUや70B級ではメモリ(3節)と量子化の対応(2節)が変わる。 推奨 想定するユースケース 決め手 気をつけること vLLM 社内チャットやRAGのPoC。公開モデル(量子化版を含む)を差し替えて評価する 公開チェックポイントをそのまま読める(2)。既定の設定で起動し、追い出しがカウンタで見える(3・4) L4のFP8はW8A16(2) SGLang 同じツール定義・システムプロンプトを先頭に付けるエージェントやバッチ処理 RadixAttention(用語)。L4でもFP8がW8A8(2)。※今回は共有部分が短く、実測ではない(5) 入力が増える(1)。割合を手で決める(3)。指標が0で出ない(5)。共有部分の長い順に処理するlpmは既定ではない(既定fcfs) TensorRT-LLM モデルを固定して長く運用する本番API 処理中のリクエストが途中でやり直しにならない(4)。量子化は最初の1回で済む 量子化を作り直す(2)。混むとTTFTが伸びるので、SLAがTTFTなら自分で測る(4)。キャッシュを消すAPIなし(5) 迷ったらvLLMで基準を作り、チェックリストで条件を揃えてから他と比べる。 付録:起動コマンド vLLM 0.31.0(bf16。AWQ・FP8はモデル名だけ替える。VLLM_SERVER_DEV_MODE=1 を付けて起動) vllm serve Qwen/Qwen3-8B --served-model-name Qwen/Qwen3-8B \ --host 127.0.0.1 --port 8000 \ --enable-auto-tool-choice --tool-call-parser hermes \ --enable-prompt-tokens-details --max-model-len 8192 SGLang 0.5.21 python -m sglang.launch_server --model-path Qwen/Qwen3-8B --served-model-name Qwen/Qwen3-8B \ --host 127.0.0.1 --port 30000 \ --tool-call-parser qwen --enable-metrics --enable-cache-report \ --mem-fraction-static 0.87 --context-length 8192 TensorRT-LLM 1.2.1(ModelOptで作ったAWQ。bf16はQwen/Qwen3-8B) echo "enable_iter_perf_stats: true" > trtllm-serve.yaml trtllm-serve ~/models/Qwen3-8B-int4-awq-modelopt \ --host 127.0.0.1 --port 8001 --backend pytorch \ --tool_parser qwen3 --config trtllm-serve.yaml --max_seq_len 8192 出典 SGLang v0.5.21:github.com/sgl-project/sglang/tree/v0.5.21 (srt/entrypoints/openai/serving_chat.py・protocol.py・usage_processor.py、srt/observability/metrics_collector.py、srt/function_call/function_call_parser.py、srt/arg_groups/memory_hook.py) TensorRT-LLM v1.2.1:github.com/NVIDIA/TensorRT-LLM/tree/v1.2.1 (tensorrt_llm/commands/serve.py、llmapi/llm_args.py、_torch/model_config.py、_torch/modules/linear.py、cpp/tensorrt_llm/executor/jsonSerialization.cpp、docs/source/features/quantization.md) ModelOpt 0.37.0:github.com/NVIDIA/Model-Optimizer/tree/0.37.0 (examples/llm_ptq/hf_ptq.py) vLLM v0.31.0:github.com/vllm-project/vllm/tree/v0.31.0 (vllm/config/cache.py、vllm/engine/arg_utils.py、vllm/model_executor/layers/quantization/fp8.py、vllm/model_executor/kernels/linear/init.py・scaled_mm/marlin.py・cutlass.py・flashinfer.py・deep_gemm.py・b12x.py、vllm/utils/flashinfer.py、vllm/platforms/cuda.py、vllm/model_executor/layers/quantization/utils/w8a8_utils.py、csrc/libtorch_stable/quantization/w8a8/cutlass/c3x/、vllm/entrypoints/openai/chat_completion/protocol.py、vllm/v1/metrics/loggers.py)、起動ログ vLLM プレフィックスキャッシュの設計:docs.vllm.ai TensorRT-LLM スケジューラの方針(GUARANTEED_NO_EVICT/MAX_UTILIZATION):nvidia.github.io SGLang サーバー引数:docs.sglang.io 、new_token_ratioはsrt/managers/schedule_policy.py nvidia/Qwen3-8B-FP8:huggingface.co PagedAttention:Kwon et al., SOSP 2023。RadixAttention:Zheng et al., NeurIPS 2024

  4. NCCL Roadmap Nov 2026 - Jan 2027 の解説 はじめに ! NCCL 奥がふかすぎる。。。。にゃ。。。 NVIDIA が公開した NCCL Roadmap: Nov 2026 - Jan 2027 (GitHub Issue #2486) から、大規模分散学習・推論に携わるエンジニアが押さえるべきポイントを解説します。前回のロードマップの解説は「NCCL Roadmap Aug-Oct 2026 の解説」、そこで挙げた項目の現在までの実装状況は「NCCL 2.32.3 でロードマップはどう実装されたか」にあります。GIN の基本は前回の解説を前提にします。 この記事では、新しい概念の GENK と Flow、AWS EFA と GIN の現状、注目機能 2 つを扱います。調査は 2026 年 10 月 29 日に行い、まだリリースされていないコードは、staging ブランチを次のリリースの候補とみなして読んでいます。 実装状況の確かめ方 NVIDIA/nccl、aws-ofi-nccl、amazon-contributing/DeepEP、NVIDIA/nccl-extensions の公開ブランチを手元に取得し、commit の履歴とソースを読んで確かめました。PR と Issue の状態 (open か、マージされたか、閉じた日) は GitHub で確かめています。記事のソースへのリンクは、確かめたときの commit に固定しています。 NCCL は master、staging、dev の 3 本のブランチを公開していて、staging には 2.32.5-1 の版番号を付けた commit があり、その後に 2.32.6a0 に上がっています。ロードマップは「Coming Soon: NCCL 2.32 Update 1」の版番号を書いていませんが、この記事では staging のコードをその候補として読みます。 今回のロードマップの項目のうち、この記事で扱うものと、前回のロードマップの近い項目は次のとおりです。対応は筆者の読みです。 今回のロードマップの項目 前回のロードマップの近い項目 General Kernels (GENK) Unify legacy kernels using Device API Flow Device API More Device API primitives GIN Backend Improvements GIN backend improvements CFT を使う階層型カーネル Compute Fabric Transport (CFT) through Device API New Enqueue Pipeline Legacy and symmetric-kernel host-side unification ロードマップには、内部のメモリの削減、RAS の進み具合の監視、RMA の profiling など、この記事で扱わない項目もあります。 Device API とは何か NCCL の API の全体像 ロードマップの項目を説明する前に、まず背景知識として NCCL の API を整理します。NCCL の API は、Host API、Device API、plugin API の 3 つに分けられます。Host API と Device API の仕様は NCCL の公式文書にあり、この記事の表はその章立てに沿っています。plugin API は公式文書の API の章には無く、リポジトリの plugins ディレクトリに plugin の種類ごとの説明があります。 API 何をするか 例 Host API CPU から呼んで、communicator (通信に参加する GPU の集まり) の作成や集団通信を NCCL に頼みます。集団通信では、NCCL がアルゴリズムを選び、NCCL のカーネルを GPU で起動します。利用者の計算は別のカーネルです ncclCommInitRank、ncclAllReduce Device API 利用者が書いた CUDA カーネルの中から、ほかの GPU との通信を直接呼びます (2.28 から)。計算と通信を 1 つのカーネルに入れられます。使う前の準備には Host API を使います GIN、LSA plugin API NCCL のほうから呼ぶ、下の部品の差し替え口です。ネットワークへの送受信や、アルゴリズムの選択の調整などを、NCCL の外の実装に任せます net、GIN。説明は plugins にあります。aws-ofi-nccl は EFA 用の plugin 誰が NCCL を呼ぶか NCCL を呼ぶのは、多くの場合アプリ自身ではなく、間に入るフレームワークやライブラリです。主な呼び手を、ソースで確かめた範囲で並べます。 呼び手 どう呼ぶか 使う API PyTorch の torch.distributed (backend "nccl") C++ の ProcessGroupNCCL が、ncclAllReduce などを直接呼びます Host API PyTorch の backend "nccl4py" (torch.distributed._nccl4py) Python で書いた backend が、nccl4py を通して呼びます。init_process_group("nccl4py") で選びます Host API nccl4py NVIDIA の公式の Python の bindings。Cython で libnccl を呼び、PyTorch の tensor や CuPy の配列をそのまま渡せます。実験的な CuTe DSL の部品もあります 主に Host API vLLM の PyNccl Python の ctypes で libnccl を読み込んで呼びます Host API PyTorch の symmetric memory の演算、DeepEP、NCCL EP 自分の CUDA カーネルの中から通信を呼びます。準備には Host API で window (次の節で説明します) を登録し、device communicator を作ります Device API と Host API aws-ofi-nccl など NCCL のほうから呼ばれます。NCCL が EFA などのネットワークに送るときの下の部品です。InfiniBand には NCCL に組み込みの経路もあります plugin API Device API は何のためにあるか Host API で通信を頼むと、通信は NCCL のカーネル 1 回分の単位になり、利用者の計算とは別のカーネルになります。計算の途中で決まった相手に少しずつ送りたい場合や、計算と通信を細かく重ねたい場合には、この単位が粗すぎます。たとえば MoE のトークンの振り分けでは、どのトークンをどの GPU に送るかが計算の途中で決まります。 Device API は、利用者のカーネルの中から、ほかの GPU のメモリへ直接データを動かせるようにするための API です。NCCL の Device API の例は、計算と通信を分けて順に行う従来のやり方に対して、カーネルの中で両方を細かく混ぜることで、待ち時間を減らし処理量を上げられると説明しています。 GPU のカーネルからほかの GPU に届く経路はいくつかあり、Device API は経路ごとに部品を用意しています。同じノードで NVLink や PCIe の P2P でつながった相手には、相手のメモリを普通の読み書きの命令で触れます (LSA)。NVLink のスイッチが複数の宛先への書き込みや足し合わせを引き受けられるシステムでは、Multimem を使います。ノードをまたぐ相手には、NIC を通して put (相手のメモリへの書き込み) や get (相手のメモリからの読み出し) を出します (GIN)。どちらも、相手が何も呼ばない片側通信です。CFT は、相手のメモリを番地 (ポインタ) で触る代わりに、CUDA が用意する宛先の番号 (logical endpoint) とオフセットを指定して put や get を出す部品で、Blackwell 以降の GPU で使えます。これらと組み合わせて使う、足し合わせやコピーの部品 (reduce と copy) も用意されています。 GIN の仕組みは、前回の解説の「GIN とは何か」で説明しました。LSA は後の GENK と Flow の節で、CFT は注目機能の節で扱います。Multimem と reduce と copy の部品は、この記事では詳しく扱いません。 どの部品を使う場合も、準備は利用者が CPU で Host API を呼んで行います。通信に使うバッファを、全ランクで同じ形に対応付けた領域 (symmetric memory window) として登録し、カーネルが使う資源を指定して device communicator を作り、それを渡して自分のカーネルを起動します。手順の例は NCCL の文書にあります。GIN を使うカーネルは、NCCL を更新したら作り直す必要があると文書は書いています。次の節からは、この Device API の上に加わる新しい部品とカーネルを見ていきます。 GENK と Flow とは何か ! GENK と Flow は、まだリリースされておらず staging と dev にだけあるコードです。この節は staging の実装から読み取った内容で、リリースまでに変わる可能性があります。staging で試すには、ビルドのときに SYM_GENK=1 を指定し、実行のときに NCCL_SYM_GENK_ENABLE=1 を設定する必要があります。 概要 Flow は、前の節の LSA と GIN の上に作られた、集団通信の部品を提供する Device API です。ロードマップは Flow を「LSA と GIN の両方で動く、高水準の Device API」と説明しています。LSA は前の節で触れた、相手の GPU のメモリを普通の読み書きの命令で触る部品です。 GENK (General Kernels) は、NCCL が自分の Ring と Tree の集団通信を、この Flow の上で作り直したカーネルの一群です。Ring では AllGather、ReduceScatter、Broadcast、Reduce、AllReduce を、Tree では AllReduce を実装しています。 前回のロードマップには「Unify legacy kernels using Device API」という項目がありました。筆者は、GENK をこの項目につながる取り組みと読んでいます。今回のロードマップは、選んだプラットフォームで GENK を既定にし、古いカーネルを置き換えるとしています。あわせて性能モデルを 1 つにまとめ、どの実装を選ぶかを同じ物差しで決めるとしています。 staging では、NCCL 自身が同じ Flow で集団通信を組んでいます。Flow がリリースに入れば、利用者も NCCL の内部と同じ部品で通信カーネルを書けます。 NCCL の集団通信には、GENK の前から 2 種類のカーネルがあります。 legacy kernels: NCCL に最初からある Ring や Tree のカーネルです。経路の違いは、CPU の側が準備する transport (NVLink や PCIe の P2P、ネットワークなど) が受け持ち、ネットワークでは CPU の proxy スレッドが NIC に渡します。 symmetric カーネル: 全ランクが同じ形で登録したバッファ (symmetric memory window) を集団通信に渡したときに使える、新しいカーネルです。相手のバッファの番地が分かるので、Device API の LSA などで相手のメモリを直接読み書きします。いまあるものは、NVLink の直結を前提に、場面ごとに作り込まれています (表の「特化した symmetric カーネル」)。 GENK は、symmetric カーネルと同じ Device API の作りで、legacy kernels と同じくらい広い経路で動くものです。Ring で隣の GPU にデータを渡す場面で比べると、次のようになります。 隣の GPU との経路 legacy kernels 特化した symmetric カーネル GENK NVLink 動く 動く 動く (LSA) PCIe の P2P 動く 使えない 動く (LSA) 別のノード (ネットワーク) 動く 一部の種類だけ動く (GIN) 動く (GIN) 表は、Hopper 以降の GPU で GENK を有効にした場合についてソースから読んだもので、動かして確かめたものではありません。PCIe の行は CUDA P2P が使える場合、GENK のネットワークの行は GIN Proxy (GPU の代わりに CPU のスレッドが NIC に送る GIN のバックエンド) が使える場合です。 特化した symmetric カーネルは、PCIe だけでつながったノードでは候補から外れます。GENK では、隣の GPU ごとに Flow が LSA か GIN かを実行時に選ぶので、同じカーネルのまま NVLink でも PCIe でもネットワークでも動きます。staging のカーネルの選択にも「特化したカーネルは NVLink の直結を要するが、GENK は CUDA P2P の LSA や GIN も使える」というコメントがあります。 利用者の側の例もあります。NCCL の Device API の例には、Transformer の RMSNorm の計算を ReduceScatter と AllGather の通信と 1 つのカーネルにまとめるものがあり、いまは LSA、Multimem、GIN を直接使って、受け取り、足し合わせ、送り出しを書いています。Flow は、NCCL 自身が集団通信に使うこの受け取り、足し合わせ、送り出しの部分を、部品として提供します。 NCCL の文書の Device API の章には、Flow と GENK の説明はまだありません。Flow の使い方は、ヘッダの説明にまとまっていて、Flow processor を足した commit が入口になります。GENK は、カーネルを足した commit と、NCCL の選択に組み込んだ commit が出発点です。 図で [API] のタグを付けた Flow processor、LSA connection、GIN connection は、アプリのカーネルからも呼べる Device API です。cost model、legacy kernels、specialized symmetric kernels、GENK は NCCL の中の部品です。 GIN の signal とは GIN の put は片側通信で、受け手は何も呼ばないので、データがいつ届いたかを自分では知れません。signal は、それを知らせるための、受け手の側に置かれた数です。送り手が put に signal を付けると、データが届いたあとに受け手の数が増え (たとえば 1 増え)、受け手はその数が目標に達するまで待ってから読みます。GIN の基本は、前回の解説の「GIN とは何か」で説明しました。 signal には strong と weak の 2 種類があります。weak signal が「読んでよい」と約束するのは、その signal を付けた put のデータだけです。strong signal は、同じ相手に同じ context (GIN の通信の通り道の 1 本) で先に出した put のデータもすべて読めると約束します。そのため、最後の put にだけ signal を付ければ済みます。 EFA の送り方の SRD は、書き込みが届く順番を保ちません。そのため、最後の put に付けた signal が届いても、前の put のデータが届いたとは限りません。EFA の上の経路のうち、GPU から直接送る EFA GDA が提供するのは weak signal だけです (次の節で説明します)。一方、Flow の GIN の接続は strong signal を必要とします。 AWS EFA 関連アップデートと現在の課題 EFA の 2 つの GIN の経路と signal の種類 ! GENK と Flow の GIN の接続は strong signal を必要とします。EFA の上で strong signal を提供するのは GIN Proxy だけで、GPU から直接送る EFA GDA は weak signal しか提供しません。そのため、EFA の複数ノードで GENK を使うなら Proxy の経路になります。 EFA で GIN を使うときは、使う経路 (GIN のバックエンド) が、カーネルの必要とする種類の signal を提供するかを先に確かめます。NCCL は、カーネルが求める種類の signal を提供できないバックエンドを使わず、提供できる別のバックエンドがあればそちらに切り替え、無ければエラーにすると説明しています。EFA で使う GIN の経路として、ここでは 2 つを比べます。1 つは NCCL の GIN Proxy で、CPU のスレッドが aws-ofi-nccl の RMA の機能を通して EFA に送ります。もう 1 つは EFA GDA で、GPU のカーネルが EFA の送信キューに直接依頼を書きます。aws-ofi-nccl の文書によると、EFA GDA は NCCL_GIN_TYPE=5 で明示したときに使われます。同じ文書は、明示しなければ NCCL 2.31 は EFA で Proxy を使うと書いています。インスタンスやドライバなどの要件も同じ文書にあります。 2 つの経路は、提供する signal が違います。NCCL の Proxy は、strong signal と VA signal (登録した仮想アドレスに置く signal) を使えると報告します。EFA GDA については、aws-ofi-nccl が strong signal も VA signal も使えないと報告し、NCCL の側も番号で指す indexed signal だけを受け付けます。EFA GDA の signal は、EFA の NIC が届いた書き込みを数えるカウンタで作られています。aws-ofi-nccl の文書は、signal と counter の合計が EFA のデバイスあたり 512 個までであることも、制限に挙げています。 図の矢印は、使い手が必要とする signal をバックエンドが提供するかを示します。矢印はソースと文書から描いたもので、どの経路が選ばれるかや、動かした結果を示すものではありません。図の DeepEP の ordered は届く順番を前提にする上流のカーネル、unordered は順番を前提にしない amazon-contributing のフォークのカーネルです。 GENK はノードをまたぐ通信に使う GIN のバックエンドとして Proxy を指定しているので、staging のコードを読む限り、GENK が EFA の上で複数ノードの通信をするなら Proxy の経路を通ります。一方、aws-ofi-nccl の文書は、EFA GDA を使うときに symmetric の GIN カーネルを無効にするよう書いていて、この設定 (NCCL_SYM_GIN_KERNELS_ENABLE=0) にすると複数ノードの GENK は候補から外れます。つまり、EFA GDA を使う設定 (NCCL_GIN_TYPE=5 と NCCL_SYM_GIN_KERNELS_ENABLE=0) では複数ノードの GENK は使われず、複数ノードで GENK を試すときは、この 2 つの環境変数を設定せずに Proxy の経路で動かします。 aws-ofi-nccl v1.22.0 で、EFA GDA の Get がタグの付いた版で使えるようになった Get は、GPU のカーネルが、ほかの GPU のメモリの中身を、持ち主に何もさせずに読み出す操作です。たとえば、手元の番号を見て、ほかの GPU が持つ表の行を取り出すような使い方ができます。Get の意義は「NCCL 2.32.3 でロードマップはどう実装されたか」で説明しました。 その記事の時点では、NCCL 2.32.3 には Get が入っていましたが、aws-ofi-nccl の側の対応はタグの無い master にしかありませんでした。2026 年 10 月 22 日 (日本時間) の v1.22.0 は、NCCL 2.32 が使うキューの形への対応と、Get の完了を数える修正を含む最初のタグです。これで、NCCL 2.32 と aws-ofi-nccl v1.22.0 という、タグの付いた版どうしに、EFA GDA の Get に要る変更がそろいました。v1.22.0 のリリースノートは Get に触れておらず、筆者は動かして確かめていません。 ただし、EFA GDA の Get には、完了を知らせる signal や counter を付けられません。読み出しが終わったかは、出した依頼をまとめて待つ Flush で確かめます。 MoE の通信ライブラリ (DeepEP と NCCL EP) の動き MoE のトークンを担当の expert に配り (dispatch)、計算結果を集める (combine) 通信のライブラリにも動きがありました。DeepEP と NCCL EP はこの同じ役割の候補で、どちらも下で NCCL を使います。DeepEP は 2026 年 9 月 29 日の V2.5 で NVSHMEM への依存を取り除き、ノードをまたぐ通信は NCCL の GIN だけになりました。要件は NCCL 2.32.3 以降です。EFA で DeepEP を動かす取り組みは amazon-contributing/DeepEP のフォークにあり、その詳細は別の記事で整理してみます。 NVIDIA の NCCL EP は NVIDIA/nccl-extensions に移り、NCCL の staging からは、nccl_ep と nccl_m2n のディレクトリが消されました。 注目機能 2 選 CFT を使う階層型カーネル CFT (Compute Fabric Transport) は NCCL 2.31 で Device API に加わった部品で、嬉しさは 2 つあります。1 つ目は、カーネルから直接届く相手を、LSA の範囲より広げられる見込みがあることです。LSA で届く相手は、CUDA P2P で相手のメモリを普通の読み書きの命令で触れる相手 (LSA team) に限られます。CFT は相手を番地ではなく CUDA が配る宛先の番号で指すので、届く範囲は、CUDA が「この番号で届く」と報告する範囲で決まります。前回のロードマップも、CFT を、logical endpoint で届く範囲の相手を宛先の番号で指す通信の形と説明していました。ただし、リリースされた版では、CFT で届く相手の集まり (CFT team) の大きさは既定で LSA team と同じです。 2 つ目は、受け手に届いたことを早く知らせる書き込みがあることです。CFT の普通の put や reduce で受け手に知らせるときは、データを書いたあと、書き込みの完了を確定させ (fence)、合図のフラグを書きます。これが counted な書き込みで、フラグの代わりに、NCCL_WIN_CFT_COUNTED を付けて登録した window の中のカウンタを使います。カウンタはデータより後に進むことが保証されているので、送り手は fence とフラグの書き込みを省けます。staging の文書は、これを低遅延のための操作と説明しています。受け手は、カウンタを見てからデータを読む間に fence を入れる必要があります。 仕組みは次のとおりです。CFT の宛先の番号は、CUDA 13.3 で加わった logical endpoint という仕組みです。NCCL は CPU の側で、宛先の番号を確保し、そこに各 GPU のバッファを結び付けておきます。カーネルは、送りたいデータを shared memory に置き、宛先の番号とオフセットを指定して put を出し、そのあと完了を確かめます。 dev では、CUDA 13.4 以降で CUDA が「logical endpoint で届く範囲」を報告すると、CFT team をその範囲から作ります。その範囲は LSA の範囲の倍数でなければならず、LSA より広い範囲を 1 つの CFT team にできる作りです。NCCL の文書は、CFT がどの物理的な経路を通るかを書いていません。NCCL EP の CFT を使う経路は、1 つの NVLink のドメインの中だけを対象にしています。 ロードマップは、この CFT を使って、AllGather、ReduceScatter、AlltoAll の階層型カーネルを作るとしています。階層型は、近い GPU の間と遠い GPU の間で通信を分ける作りで、LSA の範囲と、それより広い CFT team の範囲を使い分けることになると筆者は読んでいます。master、staging、dev を調べた範囲では、CFT の宛先の集まりの階層の種類は 2.32.3 のコードに定義されていますが、それを使う集団通信のカーネルは見当たりませんでした。 CFT とは CFT が使えるのは、ハードウェア、ドライバ、CUDA が logical endpoint と fabric 用の命令に対応したシステムだけで、例の barrier のプログラムは CUDA 13.3 と SM_100 を要件に挙げています。CFT team を CUDA の報告する範囲 (clique) から作る変更は、dev の 2026 年 9 月 28 日の commit で入り、master と staging にはまだありません。 NCCL EP には、CFT の counted な書き込みで低遅延の dispatch を送る経路が、NCCL_EP_LL_CFT=1 で選んだときだけ使われる形で、2026 年 9 月 16 日の commit から main に入っています。 New Enqueue Pipeline: 呼び出しから起動までを 3 つの層に分ける enqueue は、アプリが呼んだ集団通信や put を、カーネルやコピーの起動に変える NCCL の中の流れです。ロードマップは、これを管理 (Management)、準備 (Preparation)、スケジューリング (Scheduling) の 3 層に分け、制御の流れを簡単にし、カーネルの最適化をしやすくするとしています。 管理と準備の層を含む基本の枠組みは 2.31.2 から入っていて、その時点のスケジューリングの層は既存の経路に戻すだけでした。いまの staging と dev でも、新しい流れは既定で無効です。dev では、Preparation の tuning ですべての集団通信のアルゴリズムやプロトコルを先に選び、classification でその結果だけを使って起動の経路を振り分ける形にしました。経路ごとのスケジューラは、それぞれの仕事を stream ごとに 1 つのキューにまとめます。dev の commit では、新しい流れの symmetric のスケジューラが起動するのは特化した symmetric カーネルだけで、GENK は自分用の device communicator の準備が要るので、従来の流れの symmetric カーネルのスケジューラから起動されます。 まとめ staging の GENK は、Hopper 以降の GPU でビルドと環境変数で有効にすると NVLink、PCIe、ネットワークのどの経路でも同じカーネルが候補になり、EFA の複数ノードでは strong signal を提供する GIN Proxy の経路を使います。CFT を使う階層型カーネルは部品だけが公開コードにあり、New Enqueue Pipeline は枠組みが既定で無効のまま dev で進んでいます。

  5. ⚡ Serve LLMs faster and cheaper with vLLM's PagedAttention, continuous batching and a drop-in OpenAI-compatible API. 🔗 vllm-project/vllm Credits: vllm-project/vllm #vllm #python #llm

    3 likes
  6. Numbers from the open vLLM RFC: vllm#63626

  7. Inference on Google Cloud: Precision, Memory and Capacity on TPU, from v5e to Ironwood --- title: "Inference on Google Cloud: Precision, Memory and Capacity on TPU, from v5e to Ironwood" published: true description: "The companion to the talk: what the number format, memory bandwidth, slice shape and capacity rules do to Gemma 4 on Cloud TPU v5e and v6e, against the L4, Blackwell, MI300X and Inferentia, with the cost of each." tags: googlecloud, tpu, machinelearning, gemma cover_image: raw.githubusercontent.com --- This article is the written companion to the talk "Inference on Google Cloud". It covers serving Gemma 4 on Cloud TPU v5e and v6e through vLLM, measured on the gemma4-dev Cloud TPU test setups between July and October 2026, and compares them with the NVIDIA L4 on Google Cloud, Blackwell list prices, an AMD MI300X and AWS Inferentia. Four ideas explain most of the results: the number format the chip computes natively, the memory bandwidth that feeds it, the slice shape, and the rules that decide whether a TPU is there to rent at all. xbill9/gemma4-dev The hands-on steps run from a second repository, which starts one TPU and serves Gemma 4 on it with a single command: xbill9/gcp-inference The slides: docs.google.com --- What Is the Problem? This article compares four TPU generations, the L4 and Blackwell GPUs beside them, and every way Google Cloud prices a TPU chip-hour. The same Gemma 4 E2B serves at 144 tokens a second on a v5e in bf16 and at 243 in int8 with 4-bit embedding tables, on the same chip. A v6e-1 serves 3.3 times the L4's tokens on the same 26B checkpoint. And a zone that reports quota for 1,536 chips can still return a stockout. --- Which Generations? | Chip | HBM | Peak bf16 | Native low precision | Max pod | On-demand | |---|---:|---:|---|---:|---:| | v5e | 16 GB | 197 TFLOPs | int8, 393 TOPs | 256 | $1.20/hr | | v6e Trillium | 32 GB | 918 TFLOPs | int8, 1,836 TOPs | 256 | $2.70/hr | | v5p | 95 GiB | 459 TFLOPs | int8; fp8 at bf16 rate | 8,960 | $4.20/hr | | TPU7x Ironwood | 192 GiB | 2,307 TFLOPs | fp8, 4,614 TFLOPs | 9,216 | $12.00/hr | Google's first TPU, in its datacenters since 2015, was an 8-bit inference chip with a 92 TOPS peak, and Google announced Ironwood on 2025-04-09 as "the first Google TPU for the age of inference". The gemma4-dev test setups measured v5e and v6e. v5p and TPU7x rows come from Google's documentation only. vLLM reaches the TPU through tpu-inference, a JAX backend. XLA compiles one program per input shape, which makes the compile the largest part of a cold start. --- At This Point You Should Have… - The gcloud CLI with its alpha and beta components, signed in with gcloud auth login and gcloud auth application-default login - A project with Cloud TPU quota for one v5e chip in us-west4-a - python3 with pip install 'google-adk[extensions]' google-genai mcp - A clone of xbill9/gcp-inference --- Step 1 — One TPU, One Command demo.sh asks for a TPU v5e-1 as a flex-start request in us-west4-a, waits for the node, opens an SSH tunnel to vLLM on the TPU's port 8000, waits for the model to load and compile, then runs a Google ADK agent against it. Flex-start stops the node by itself after 4 hours. On a TPU that is already up, ./demo.sh again skips the first two steps: ./demo.sh again == [0m01s] 3/5 Connecting to it over SSH (the demo reaches vLLM on the TPU's port 8000 through a tunnel) [0m05s] SSH works [0m11s] tunnel open: localhost:8005 -> TPU port 8000 == [0m11s] 4/5 Starting Gemma on vLLM (first start downloads and compiles: several minutes) [0m30s] vLLM is answering 20:21:25Z PASS tpu chat 0.198s; tool run filters=['id >= 10'] quoted=8 3.71s READY == [0m37s] 5/5 The demo: Gemma on the TPU counts ids with the count_ids tool from the MCP server Question: How many of the ids are 10 or more? Answer with the number. Ids: [0, 2, 3, 20, 21, 22, 23, 10, 11, 12, 13] True answer: 8 tools: mcp tpu run 1/3 ok quoted=8 filters=['id >= 10'] 3.87s tpu run 2/3 ok quoted=8 filters=['id >= 10'] 0.51s tpu run 3/3 ok quoted=8 filters=['id >= 10'] 0.49s backend runs correct right-filter errors median s filters sent tpu 3 3/3 3/3 0 0.51 id >= 10 The agent is an ADK LlmAgent whose model is LiteLlm pointed at vLLM's OpenAI-compatible API, and an ADK McpToolset starts mcp_server.py over stdio and offers it one tool. The model never counts. It calls count_ids on that MCP server, which returns the exact count, and each run is graded on the filter it sent as well as the number it quoted. Gemma 4 E2B on the v5e sent id >= 10 in all three runs. The node serves google/gemma-4-E2B-it in bf16 with vllm/vllm-tpu:v0.30.0, one chip, no sharding. --- Step 2 — What the 16 GB Chip Holds vLLM logs the chip's memory as it starts. In order: Init worker | ... | hbm=[(0.0, 15.75)]GiB Init model | hbm=[(8.97, 15.75)]GiB Memory statistics | total_hbm_limit_gb=15.75GiB | total_hbm_limit_cap_gb=14.49GiB | total_hbm_used_gb=8.97GiB | total_hbm_avail_gb=5.52GiB TPU KV cache size: 321,280 tokens, Maximum concurrency for 8,192 tokens per request: 39.22x Init kv-cache | num_total_layers=15 | num_blocks=[1255, ...] | regular_attn_shape=(num_blocks, (256, 1, 2, 256)) | regular_attn_dtype=bfloat16 | hbm=[(14.51, 15.75)]Gb | HBM on one v5e chip | GiB | |---|---:| | Weights, bf16 | 8.97 | | vLLM's ceiling (92% of 15.75) | 14.49 | | Left for the KV cache | 5.52 | | In use after the KV cache is allocated | 14.51 | | Never allocated | 1.24 | Each of the 15 cached layers holds 1,255 blocks of 256 tokens, one KV head of dimension 256, in bf16. That is 1,024 bytes per token per layer, 15,360 bytes per token across all 15, and 321,280 token slots in all. The model's config lists 35 layers with num_kv_shared_layers: 20: 20 layers reuse another layer's keys and values, so the cache holds 15. --- Step 3 — The Full 131,072-Token Context Gemma 4 E2B's config allows 131,072 positions. vLLM sizes its KV cache from the memory left after the weights, so raising --max-model-len from 8,192 to 131,072 changes the concurrency and leaves the cache the same size. To change a flag on a running TPU, replace the container: gcloud compute tpus tpu-vm ssh gcp-inference-tpu-node --project <tpu-project> --zone us-west4-a --command \ 'sudo docker rm -f vllm && sudo docker run -d --name vllm --restart unless-stopped --privileged --net=host \ -v /dev/shm:/dev/shm --shm-size 10gb -e HF_HOME=/dev/shm vllm/vllm-tpu:v0.30.0 \ vllm serve google/gemma-4-E2B-it --served-model-name gemma --max-model-len 131072 --port 8000 \ --max-num-batched-tokens 4096 --enable-auto-tool-choice --tool-call-parser gemma4' Memory statistics | total_hbm_limit_gb=15.75GiB | total_hbm_limit_cap_gb=14.49GiB | total_hbm_used_gb=8.97GiB | total_hbm_avail_gb=5.52GiB TPU KV cache size: 321,280 tokens, Maximum concurrency for 131,072 tokens per request: 2.45x init engine (profile, create kv cache, warmup model) took 503.54 s (compilation: 413.41 s) Application startup complete. | --max-model-len | 8,192 | 131,072 | |---|---:|---:| | KV cache, tokens | 321,280 | 321,280 | | Full-length requests at once | 39.22 | 2.45 | | Engine start | 502.84 s | 503.54 s | | of which XLA compile | 408.59 s | 413.41 s | One full-length request needs 1.875 GiB of KV cache. scripts/longctx.py in tpu-survey buries a passphrase at a chosen depth in up to 124,949 tokens of filler, starts each prompt with a unique line so vLLM's prefix cache cannot reuse an earlier run, and asks for the passphrase: $ python3 scripts/longctx.py $TPU_URL 8000 0.5 {"prompt_tokens": 7949, "completion_tokens": 10, "depth": 0.5, "seconds": 0.61, "prompt_tokens_per_second": 13110.4, "answer": "violet-harbor-7341", "found": true} $ python3 scripts/longctx.py $TPU_URL 120000 0.1 {"prompt_tokens": 124949, "completion_tokens": 10, "depth": 0.1, "seconds": 9.53, "prompt_tokens_per_second": 13116.9, "answer": "violet-harbor-7341", "found": true} $ python3 scripts/longctx.py $TPU_URL 120000 0.5 {"prompt_tokens": 124949, "completion_tokens": 10, "depth": 0.5, "seconds": 9.54, "prompt_tokens_per_second": 13102.3, "answer": "violet-harbor-7341", "found": true} $ python3 scripts/longctx.py $TPU_URL 120000 0.9 {"prompt_tokens": 124949, "completion_tokens": 10, "depth": 0.9, "seconds": 9.41, "prompt_tokens_per_second": 13274.6, "answer": "violet-harbor-7341", "found": true} $ python3 scripts/longctx.py $TPU_URL 140000 0.5 HTTP 400: {"error":{"message":"This model's maximum context length is 131072 tokens. However, you requested 16 output tokens and your prompt contains at least 131057 input tokens, for a total of at least 131073 tokens. ..."}} The passphrase comes back at 10%, 50% and 90% depth of a 124,949-token prompt, in 9.4 to 9.5 seconds each, about 13,100 prompt tokens a second as timed from the client. vLLM's own log reads 12,495 tokens a second, a 0.0% prefix cache hit rate, and 38 to 39% of the KV cache in use while the request runs. A prompt past the limit is refused before it reaches the chip. --- 🔎 Tip: Raise the Context on the Chip You Have Flex-start capacity is granted per request. Replacing the container keeps the node, and its 4-hour clock, and costs one engine start, 503.54 s on this v5e. Deleting the node to relaunch it means a new flex-start request, and a new wait for capacity. --- Step 4 — Precision: What the Chip Computes A smaller format saves memory and bandwidth on every chip. It saves compute only where the matrix unit multiplies that format natively. | Format | v5e | v6e | v5p | TPU7x | |---|---|---|---|---| | bf16 | native | native | native | native | | int8 | 2x bf16 | 2x bf16 | native | native | | fp8 | storage | storage | bf16 rate | 2x bf16 | | int4 | storage | storage | storage | storage | "Storage" means the weights widen to bf16 before every matrix multiply. On v5e that ordering shows up directly in E2B throughput at one stream: | E2B format on v5e-1 | tok/s | |---|---:| | bf16 | 144 | | 4-bit QAT + 4-bit embeddings | 150 | | fp8 | 182 | | fp8 + 4-bit embeddings | 203 | | int8 W8A8 | 220 | | int8 + 4-bit embeddings | 243 | Accuracy holds: 0.683 for bf16 against 0.677 for int8 with 4-bit embeddings, on a 3,880-record suite. At 12B, fp8 serves 518 tok/s at 16 streams against 624 for int8 at the same file size. On v6e the 4-bit path pays off through a fused kernel. Gemma 4 26B-A4B in 4-bit QAT W4A16 serves 1,283 tok/s at 16 streams against 668 for fp8, in 17.43 GiB of weights against 27.99 GiB, and holds 53,888 KV-cache tokens against 3,456. Accuracy is 75.3% against 76.0%. W4A16 needs tpu-inference PRs #3299, #3653 and #3660 on top of the stock image. --- Step 5 — Memory and Bandwidth Each generated token reads every weight once, so bandwidth sets the ceiling for one request. | Chip | Memory | Bandwidth | E2B bf16 tok/s, 1 stream | tok/s per TB/s | |---|---:|---:|---:|---:| | TPU v5e | 16 GB | 859 GB/s | 120 | 140 | | TPU v6e | 32 GB | 1,638 GB/s | 199 | 122 | | TPU v5p | 95 GiB | 2,765 GB/s | not measured | — | | TPU7x | 192 GiB | 7,380 GB/s | not measured | — | | NVIDIA L4 | 24 GB | 300 GB/s | 46 | 154 | The three measured chips land between 122 and 154 tokens a second per TB/s. Google quotes v5e bandwidth as 800 GiBps, which is 859 GB/s. The L4 figure was measured on an AWS g6.2xlarge, the same chip as Google Cloud's g2. --- Step 6 — Topologies and Slices Weight size picks the slice. Every Gemma 4 size serves on one host: | Model | v5e-1 | v6e-1 | v6e-4, TP=4 | |---|---|---|---| | E2B | bf16 or int8 | bf16 | — | | E4B | int8 or 4-bit; bf16 does not fit | bf16 | — | | 12B | int8 or 4-bit | bf16, 22.18 GiB | — | | 26B-A4B | 4-bit, 201 tok/s | 4-bit, 1,283 tok/s | bf16, 1,982 tok/s | | 31B | does not fit | 4-bit, 500 tok/s | bf16, 2,031 tok/s | Small models scale out. On a v5e-4, four one-chip E2B engines serve 1.39x the output of one TP=4 engine at 16 streams, 2.52x at 64 and 3.36x at 256. E2B has one KV head, so tensor parallelism keeps four copies of the KV cache. 12B gains from TP=4: 2.25x the v5e-1 output at one stream, and 122,624 KV tokens against 9,728. --- 🔎 Tip: Quota Lets You Ask, Availability Decides Quota is permission to request a TPU. Availability decides whether the request gets hardware, through three checks: hardware in the zone, the provisioning model offered there, and capacity at that moment. 37 zones report v6e quota; 18 offer v6e-1 hardware. Three of five zones holding 1,536 chips of quota returned stockout. us-central1-b has hardware, quota and a flex-start price, and rejects flex-start. Check capacity with a spot request: it fails fast with "reason: stockout", where a flex-start request with no quota queues as PENDING exactly like a stockout. Ask for fewer chips. In europe-west4-a a v6e-8 flex-start request expired after 26 minutes, and a v6e-4 was granted at once less than an hour later. --- Step 7 — Against Other Accelerators On the same 26B 4-bit checkpoint, with the same flags and client: | | NVIDIA L4 | TPU v6e-1 | |---|---:|---:| | Output tok/s, 16 x 256 tokens | 393.7 | 1,283.3 | | KV cache tokens | 17,990 | 53,888 | | 3,880-record suite, wall time | 139 s | 58 s | | 4-bit kernel | Marlin | gmm_v2 | | Software | stock vLLM 0.30.0 | three tpu-inference patches | | List price per hour | $0.71 (g2-standard-4) | $2.70 | Cloud Run serves the L4 as a managed service: one L4 per instance, weights mounted from Cloud Storage at /mnt/models, a startup check that waits 180 s before its first check, and a default that scales between zero and one instance, so an idle service means a cold GPU start. Blackwell on Google Cloud lists above v6e per accelerator-hour: an RTX PRO 6000 on g4-standard-48 at $4.50 on-demand and $2.25 flex-start, and a B200 at $8.06 per GPU flex-start ($64.44 for the 8-GPU a4-highgpu-8g, divided by 8). Both are list prices only, with no measurements in this article. Per dollar, on E2B bf16 with vllm bench serve at 1,024 prompt and 128 output tokens, v5e at its $0.58 spot price leads to 16 streams and the MI300X at $1.99 leads at 64. The v6e-1 is priced at the europe-west4 on-demand rate of $2.97: | tok/s per $/hr | MI300X ($1.99) | v5e-1 spot ($0.58) | v6e-1 europe-west4 ($2.97) | |---|---:|---:|---:| | 1 stream | 153 | 208 | 67 | | 4 streams | 487 | 711 | 233 | | 16 streams | 1,333 | 1,572 | 587 | | 64 streams | 2,713 | 1,972 | 719 | Each software stack fails in its own way: | | TPU, vLLM + JAX | CUDA, L4 | ROCm, MI300X | Neuron, inf2 / trn1 | |---|---|---|---|---| | Start to healthy, E2B | 510 s v6e, 986 s v5e | 334 s | about 160 s | 171 s on trn1 | | Gemma 4 support | 4-bit needed patches | 26B 4-bit loads stock | fp8 only as fnuz | vLLM-Neuron cannot load it | | Hazard | OOM after a long compile | sm_89 missing; runs sm_86 code | AITER flag 0.97 to 0.99x | large gather returns zeros, HTTP 200 | --- 🔎 Tip: Cache the Compile XLA compile is 81 to 91% of vLLM's engine start for E2B: 329 of 404 seconds on v6e-1 and 738 of 814 on v5e-1, inside 510 and 986 seconds from container start to healthy. On today's v5e-1 with vllm/vllm-tpu:v0.30.0 the engine start was 503.54 seconds, 413.41 of them compile. A mounted compile cache cut a v5e restart from 857 to 497 seconds, 42%. 31B weights load in 5.6 minutes from in-region Cloud Storage against 60 to 90 from Hugging Face. Serving flags that work on GPUs fail late on TPU, after the compile. --gpu-memory-utilization 0.95 failed on v5e after a 691-second compile, twice; 0.92 serves. --kv-cache-dtype fp8 logs fp8 on v6e and allocates bf16. The vllm/vllm-tpu:nightly tag broke two 4-bit builds on 2026-10-02, so pin an image digest. --- Step 8 — What Each Chip Costs | $ per chip-hour | On-demand | Flex-start | Calendar | 1-yr CUD | 3-yr CUD | Spot read | |---|---:|---:|---:|---:|---:|---:| | v5e | $1.20 | $0.60 | $0.84 | $0.84 | $0.54 | $0.58 | | v6e Trillium | $2.70 | $1.35 | $1.89 | $1.89 | $1.22 | $1.40 | | v5p | $4.20 | $2.10 | $2.94 | $2.94 | $1.89 | — | | TPU7x Ironwood | $12.00 | $6.00 | $8.40 | $8.40 | $5.40 | — | Flex-start halves on-demand on every generation and can undercut spot: v6e in us-east5 lists $1.35 flex-start against a $1.40 spot price. Flex-start runs 10 minutes to 7 days and bills until deleted. Utilization sets the price per token. A v6e-1 at the europe-west4 on-demand rate of $2.97 an hour serves E2B at $4.14 per million output tokens with one stream, $0.70 with eight and $0.55 with 32, at 1,024-token prompts. A v5e at the $0.60 flex-start rate goes from $1.31 per million at one stream to $0.09 at 64, at 128-token prompts. --- Compare and Contrast | Option | Precision served | Bandwidth | Measured result | List $/hr | Software | |---|---|---:|---|---:|---| | 🥇 TPU v6e-1 | 4-bit QAT W4A16 | 1,638 GB/s | 26B at 1,283 tok/s | $2.70 | tpu-inference, patched | | 🥈 TPU v5e-1 | int8 W8A8 + 4-bit embeddings | 859 GB/s | E2B at 243 tok/s, one stream | $1.20 | tpu-inference | | 🥉 NVIDIA L4 | 4-bit QAT | 300 GB/s | 26B at 394 tok/s | $0.71 | stock vLLM | | TPU7x Ironwood | fp8 native | 7,380 GB/s | not measured | $12.00 | allowlisted | | Blackwell B200 | fp4 native | — | not measured | $8.06 flex, per GPU | not measured | --- So, Which One? For E2B or E4B at the lowest cost, run v5e-1 in int8 with one engine per chip, on spot or flex-start. For 12B to 31B on one chip, run v6e-1 in 4-bit QAT. For bf16 26B or 31B with long context, run v6e-4 with TP=4. For stock software and the fastest boot, run the L4, on Compute Engine or on Cloud Run. TPU7x brings fp8 math and 192 GiB per chip, behind an allowlist. Whichever you pick: check capacity with a spot request, ask for the smallest slice that fits, pin the image digest and mount the compile cache. --- Summary The goal of this article was to serve Gemma 4 on a Cloud TPU in one command, then explain the results across TPU generations through native precision, memory bandwidth, slice shape and capacity. The key to the solution was measuring each claim on real hardware, with every ratio and price computed in code. The results were: - 🟢 One v5e chip holds Gemma 4 E2B's full 131,072-token context: a 321,280-token KV cache beside 8.97 GiB of bf16 weights, and a passphrase found at 10%, 50% and 90% depth of a 124,949-token prompt in 9.4 to 9.5 s. - 🟢 The agent sends the right filter: 3 of 3 runs on the v5e called count_ids with id >= 10 and quoted 8. - 🟢 int8 is the fast format on v5e: with 4-bit embedding tables it serves E2B at 1.7x bf16, with accuracy within a point. - 🟢 v6e-1 serves 3.3x the L4's tokens on the same 26B 4-bit checkpoint, with 3x the KV cache. - 🟢 Flex-start halves on-demand on every generation, and can undercut spot. - ⚠️ Cold start takes 8.5 to 16.4 minutes, most of it XLA compile; cache it. - 🟢 One-stream decode follows bandwidth: 122 to 154 tok/s per TB/s on v5e, v6e and the L4. - ❌ Quota is permission to request; availability decides the grant: 37 zones report v6e quota, 18 have v6e-1 hardware, and zones holding full quota still return stockout. Scope: the hands-on steps ran on one flex-start v5e-1 in us-west4-a on 2026-10-25, google/gemma-4-E2B-it in bf16 on vllm/vllm-tpu:v0.30.0, three demo runs and one long-context request per depth. The rest covers Gemma 4 E2B to 31B on Cloud TPU v5e and v6e, measured on the gemma4-dev test setups between July and October 2026 with vLLM and tpu-inference; L4 on Google Cloud g2 and AWS g6; MI300X on DigitalOcean; Inferentia2 and Trainium on AWS. The cross-vendor tokens-per-dollar table mixes vLLM builds, hosts and repeat counts, so it shows direction. v5p, TPU7x and Blackwell figures are Google's published specifications and list prices, read on 2026-10-12 and re-read on 2026-10-25 with the same values; spot prices come from the Cloud Billing Catalog on recorded dates. The strategy for serving Gemma 4 on Google Cloud from v5e to Ironwood was validated with an incremental step by step approach. --- References - gemma4-dev | GitHub - gcp-inference | GitHub - tpu-survey, long-context test and evidence | GitHub - Gemma 4 E2B-it | Hugging Face - Inference on Google Cloud slides | Google Slides - Inference on AWS slides | Google Slides - tpu-inference | GitHub - Cloud TPU documentation | Google Cloud - TPU7x (Ironwood) | Google Cloud - Cloud TPU pricing | Google Cloud - Accelerator-optimized machine pricing | Google Cloud - In-Datacenter Performance Analysis of a Tensor Processing Unit | arXiv

    8 reactions · 1 comment
  8. PyTorch-native TPU back end for vLLM | Discussion

    1 like
  9. PyTorch-native TPU back end for vLLM vllm-project/vllm-torchtpu

  10. PyTorch-native TPU back end for vLLM comments · posted on 2026.10.19 at 18:34:38 (c=0, p=4)

Trending awards, every completed day and week
Gold
01st place finishes
Silver
22nd place finishes
Bronze
33rd place finishes
Awarded repositories
1Owned by vllm-project now

Repositories

Ordered by gold, then silver, then bronze.

  1. vllmBest: 2nd by stars, 991 stars023

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.