
三顆模型的 checkpoint tensor 加起來約 7.85 GiB。卡有 12 GiB,看起來很寬鬆。
再替三個 vLLM instance 各留一份 runtime 峰值,預算就先膨脹到 9.65 GiB——卡的八成,而 KV 還沒進預算。
12 GiB 看似還剩 2.35 GiB,但其中 0.84 GiB 得留給三份落在 util 管理域外的 CUDA context,另外 0.36 GiB 完全不分配給誤差與碎片。剩下的 1.15 GiB 裡,預計分給 cache 的只有 1.03:LLM 0.72、embedding 0.31;reranker 是 encoder,那 0.12 是餘量不是 KV。
昨天那把刀砍的是時間。今天換一種資源:顯存不會因為你聰明就變多,它只能分。
一次 RAG 請求要先把問題變向量(embedding),撈 top-k,重排一次(reranker),最後才輪到 LLM 生成。三個模型、三份權重、三份顯存。
大多數教學只教你把 LLM 伺服起來;embedding 和 reranker 呢?「反正很小,隨便塞。」
於是常見劇本是:vLLM 跑得好好的,加 reranker 那天整台炸掉。因為不設 --gpu-memory-utilization,它就照預設值圈走整張卡的九成——注意是「整張卡」不是「剩下的」。這是 per-instance 的上限,它不管同一張卡上還有沒有別人
⚠️ 那個預設值改過:v0.19.1 以前是 0.9,v0.20.0 起是 0.92。別背數字,跑一次 vllm serve --help 看你裝的那版印什麼。
vLLM 分配的方向跟直覺相反。它不是「你要多少 KV 我給你多少」,而是先圈地、再看剩多少:
管理域 = 卡的總顯存 × util
KV 剩額 = 管理域 − 權重 − peak activation − non-Torch − CUDA Graph
**KV 是剩額,不是配額。**扣的不只是 activation:non-Torch(NCCL、attention backend buffer)與 CUDA Graph 預留都算在裡面。啟動時 vLLM 會分列印出來,那份 log 才是你真正的帳。
單位全表統一 GiB——這張卡的「12GB」是 12 GiB = 12,288 MiB。
| 服務 | 模型 | util | 管理域 | 權重 | non-KV runtime | = 剩額 |
|---|---|---|---|---|---|---|
| LLM | Qwen3-8B-AWQ | 0.60 | 7.20 | 5.68 | 0.80 | 0.72(KV,一條 8K FP8) |
| Embedding | Qwen3-Embedding-0.6B | 0.16 | 1.92 | 1.11 | 0.50 | 0.31(KV,1K 約 2.8 條) |
| Reranker | bge-reranker-v2-m3 | 0.14 | 1.68 | 1.06 | 0.50 | 0.12(餘量,encoder 無 KV) |
| — | 預算外:三份 CUDA context | 0.07 | 0.84 | — | — | — |
| — | 未分配:驅動、誤差與碎片 | 0.03 | 0.36 | — | — | — |
| 合計 | 1.00 | 12.00 |
【權重是 tensor payload(HF ?blobs=true 實抓);剩額由前三欄相減得出】

圖 1:橘色三段加上預算外的 0.84,合計 2.64 GiB——卡的 22%,裡面沒有一個 byte 是模型權重。
**最後兩列才是這張表能不能用的關鍵。**這是紙上預算:runtime 與 context 兩欄是估算,4070 Ti 實際可見容量也未必剛好 12,288 MiB。把 util 加到 1.00、餘量壓成 0,那不叫預算。所以這裡犧牲「兩條滿額 8K」,換一條 8K 加餘量。
reranker 那格的 0.12 也不是 KV:它是 XLM-R 出身的 cross-encoder,一次前向吐一個分數,不生成、不留 KV,本來就不配 KV block。同樣的減法對 embedding 就不同了——它是 decoder 出身,剩額歸零代表 KV block 數算出 0,vLLM 會在啟動時直接丟 No available memory for the cache blocks。差別不在剩額大小,在剩額歸零的後果。

圖 2:同一顆 reranker,checkpoint 2.12 GiB、FP16 tensor payload 1.06 GiB。
bge-reranker-v2-m3 的 checkpoint 存成 float32,而 vLLM 對 pooling model 會自動降成 fp16(_resolve_auto_dtype())。抄 HF 頁面的大小編預算,這一格會多編一倍。1.06 是 tensor payload,實機載入還要再加上 allocator 對齊與 buffer。
Qwen3-8B 是 36 層全注意力、8 個 kv_heads、head_dim 128,config 裡沒有 layer_types 也沒有 sliding_window,所以 Day 09 那條 GQA 公式直接套:144 KiB/token @FP16。一條 8K 滿額序列就是 1.125 GiB,而這格只分到 0.72。

圖 3:同一格預算,FP16 過 4K、過不了 8K;FP8 過 8K。
所以開 --kv-cache-dtype fp8,每 token 砍成 72 KiB,一條 8K 是 0.5625 GiB,進得去。
代價:官方在幾個 reasoning benchmark 上量到平均差約 1–2 分,長 context 實驗則依模型落在九成多的基準恢復率【vllm-project.github.io/2026/04/22/fp8-kvcache.html】。那不是 Qwen3-8B-AWQ 在你的 RAG 資料上的保證,仍要自己重跑 eval。
⚠️「一條」是吃滿 8K 的最壞情況。Day 14 花了整篇講這件事:實際 RAG 序列短得多,同一份預算裝得下的條數會多得多。
non-KV runtime 那三格錨在 Day 03 抓過的 activation 峰值 0.5–1 GB,而且 non-Torch 與 CUDA Graph 也擠在同一格裡。
至於 CUDA context:每個碰 CUDA 的 process 驅動都要先建一份,它落在 util 預算之外——vLLM 的記憶體快照是在 device 初始化之後才拍的。所以三個服務的 util 不能加到 1.00。
這兩欄都會因版本、backend 與系統而變,表裡先放一組工作估值,另外保留 0.36 GiB 給誤差與碎片。它們的作用不是保證啟得起來,而是提醒你:權重與 KV 之外,還有一筆不能忽略的 process 成本。
多數「顯存夠不夠」的算法只算權重加 KV,那是單服務的世界。共存時,process 數本身就是成本。
以下是依預算表換算的起跑參數,不保證每個 vLLM/driver 組合都能原樣啟動。vllm serve 是 blocking process,三個 terminal 各跑一條;兩個 pooling 服務的 activation 峰值不只看 max-model-len,也看批次大小,所以一起釘住:
# Terminal 1
CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-8B-AWQ \
--gpu-memory-utilization 0.60 --max-model-len 8192 \
--kv-cache-dtype fp8 --port 8000
# Terminal 2
CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-Embedding-0.6B \
--runner pooling --convert embed \
--gpu-memory-utilization 0.16 \
--max-model-len 1024 --max-num-batched-tokens 1024 --port 8001
# Terminal 3
CUDA_VISIBLE_DEVICES=0 vllm serve BAAI/bge-reranker-v2-m3 \
--runner pooling --gpu-memory-utilization 0.14 \
--max-model-len 1024 --max-num-batched-tokens 1024 --port 8002
(--task 已於 v0.13.0 從 CLI 移除,改用 --runner / --convert。)
96GB 卡還能用 MIG 把容量與故障域切開,超線只死自己;12GB 卡沒有硬體牆,只能靠每個 instance 的 flag 記帳。
一句話總結階級差:12GB 買到的是「算好帳可以住」,96GB 買到的是「住的時候有牆」。
**這組模型以 12 GiB 作為紙上起點。**8 GiB 不是重分 util 能救的:LLM 的 0.60 管理域只有 4.8 GiB,連 5.68 GiB 的權重都裝不下;而三顆模型的 tensor payload 就佔掉 8 GiB 卡的 98%,還沒算任何 runtime 與 context。更小的卡要換模型或減少 process,不是調 util。
我的 T2 是 Windows + WSL 桌機,而這張表的前提是 headless;桌面本身要吃掉 0.3–0.5 GiB,要重現得先扣掉再重算三個 util。
Mac 讀者一樣要編預算,那條線是 Day 07 的 recommendedMaxWorkingSetSize,差別是沒有隔離、也沒得切。
util × 總顯存 圈地,扣掉權重、activation、non-Torch 與 CUDA Graph,剩下的才給 KV。--gpu-memory-utilization 是 per-instance 上限、互不知情。bge-reranker-v2-m3 的 checkpoint 是 fp32,vLLM 降成 fp16 只剩一半。明天開第四張地圖。快,救回來了;但快而答錯,等於零。Day 22〈答錯不是模型笨:RAG 準確度的七層歸因〉——一次 RAG 答錯,兇手可能躲在七層裡的任何一層,我們一層一層抓。
咱們明天見。