01

總覽與定位

為什麼 LLM 特別難 serve:自回歸、串流、GPU 記憶體與延遲/吞吐的交錯約束。

RAG 與 Agent 討論的是「怎麼組上下文、怎麼呼叫模型」;本篇切到純後端 infra:模型權重已就緒,你要把它變成可水平擴充、可觀測、可對 SLA 負責的 Inference Serving。不談訓練、微調或資料集——只談請求進來之後,GPU 上發生什麼、哪裡會爆。

自回歸不可並行到「一個答案」
下一個 token 依賴前面所有 token。使用者看到的是串流(streaming),系統看到的是逐步 decode 的長尾工作。
狀態比權重更吃顯存
Attention 的 KV Cache 隨序列長度與並發線性成長;顯存耗盡前,吞吐早已被排程策略綁死。
兩個互相打架的 SLA
TTFT(Time to First Token)要短、互動感才好;throughput(tokens/s 或 req/s)要高,單位 GPU 成本才划算。
Prefill
吃完整 prompt
建 KV · 定 TTFT
→
Decode
逐 token 產出
串流回客戶端
→
KV Cache
每層 K/V
佔 GPU HBM
→
Batching
連續批處理
抬吞吐、管排隊

看第一個盒與第三個盒的關係:Prefill 決定 TTFT,但它寫出的 KV Cache 會一路佔著顯存直到請求結束——所以「首 token 快不快」與「這台卡還能接幾個人」是同一段計算的兩個面向。

座標與鄰近主題的切分

建議先讀過 RAG 架構 再進來(軟依賴:理解「長 prompt 從哪來」)。本篇不重複檢索/chunking;也不談 Agent loop 或 tool calling——那些是編排層。這裡只回答:同一個模型權重,如何在 GPU 上穩定地服務多用戶推論。

一句話答法LLM serving 難在自回歸 + KV Cache 吃顯存 + TTFT/吞吐互相拉扯;工程核心是 Prefill/Decode 分離、連續批處理與記憶體感知排程。
02

Prefill、Decode 與 Token Streaming

一次請求在時間軸上拆成兩段計算型態,串流只是 Decode 階段對外的呈現。

Prefill(提示消化)
COMPUTE-BOUND · 定 TTFT
  • 一次處理完整 prompt(含 system/RAG context)
  • 為每個 transformer 層寫入初始 KV Cache
  • 通常可用高度並行的矩陣運算吃滿 GPU
  • 長度一拉長(長 context),TTFT 幾乎線性惡化
Decode(自回歸產出)
MEMORY-BOUND · 定體感速度
  • 每步只產出約 1 個 token,再 append 進 KV
  • 算力密度低:瓶頸常是讀取巨大 KV/權重頻寬
  • 透過 SSE/WebSocket/gRPC stream 推給客戶端
  • 結束條件:EOS、max_tokens、或客戶端取消

產品面的「打字機效果」來自 Decode 串流;Infra 面則必須分開優化兩段:Prefill 要降 TTFT,Decode 要抬 TPOT(Time Per Output Token)與整體 tokens/s。把兩者綁在同一個同步 batch 裡,往往會讓長 prompt 請求拖死短請求的首 token。

時間步
GPU 在算什麼
KV Cache 怎麼變
客戶端看到什麼
prefill
一次吃完整 prompt(system + RAG context + 提問)
每一層寫入初始 K/V,長度=prompt 長度
還沒有任何字——TTFT 正在計時
decode 1
讀權重與整段 KV,算出第 1 個 token
append 1 個 token 的 K/V
首字出現 → TTFT 在此定格
decode 2…n
每步只產 1 個 token,重複 n−1 次
每步 +1 token 的 K/V,隨答案長度線性成長
逐字串流,節奏由 TPOT 決定
終止
命中 EOS/max_tokens/客戶端取消
整段 KV 一次釋放,空間還給別的請求
串流結束

看第三欄與第四欄的落差:KV 從 prefill 那一刻就滿載寫入,客戶端卻要到 decode 第 1 步才看到第一個字——這一格的時間差就是 TTFT,也是本篇所有排程取捨的起點。

陷阱把「模型延遲」當單一數字

面試或監控若只報一個 p99 latency,會掩蓋真相:使用者真正在意的是 TTFT 與後續 token 節奏。長答案請求的 wall-clock 幾乎必然高,不代表服務壞了。

一句話答法Prefill 吃完整 prompt、決定 TTFT;Decode 逐步產 token 並串流;兩者計算特性不同,必須分開量測與排程。
03

Continuous Batching:把 GPU 從「等人」變成「一直算」

靜態 batch 等齊才跑;連續批處理讓請求動態進出 iteration,抬高吞吐。

傳統離線推論習慣「湊滿 B 筆再跑」:對 LLM 幾乎不可用——有人要 8 個 token、有人要 800 個,齊步走會讓短請求乾等、長請求佔滿 slot。現代 serving runtime(vLLM、TGI、TensorRT-LLM 等)的核心是 Continuous Batching(又稱 iteration-level scheduling):

  • 每個 decode step 組成一個「當前活躍集合」
  • 請求可在 step 邊界 加入(新 prefill 完成)或 離開(EOS/取消)
  • GPU 幾乎每一步都在算,而不是等最慢那筆結束
策略 排程粒度 短請求 TTFT GPU 利用率 實作複雜度
靜態 Batch 整批起迄對齊 差(等人) 低~中 低
Continuous Batching 每個 decode step 較穩 高 中~高
Prefill/Decode 分離佇列 兩類 worker/優先權 可調優 高(需配額) 高

Batching 不是越大越好:batch 越大吞吐通常越高,但單請求的 TTFT/TPOT 會被同批鄰居拖累。

關鍵批處理換的是「攤提」

權重載入與 kernel launch 成本被多請求攤提;Decode 又偏 memory-bound,多序列一起算能更有效吃滿記憶體頻寬。代價是排程、分頁 KV、與公平性(長請求饿死短請求)都變難。

一句話答法Continuous batching 在每個 decode step 動態重組活躍請求,避免靜態 batch 齊步走,是現代 LLM serving 抬吞吐的基本盤。
04

KV Cache 與 GPU 記憶體瓶頸

顯存同時要扛權重、啟動配置與隨序列成長的 K/V;爆了就只能拒單或換頁。

對 decoder-only Transformer,每個 token、每一層都要快取 Key/Value,後續 decode 才不必重算整段歷史。粗估:

KV_bytes ≈ 2 × layers × kv_heads × head_dim × seq_len × dtype_bytes × batch

因此並發 N 個長對話時,KV 往往比「再塞一個 replica」更先撞牆。工程上常見手段:

  • PagedAttention / 分頁 KV:像 OS 分頁一樣按 block 配置,減少碎片、支援動態長度
  • Prefix caching:共用 system prompt/RAG 前綴的 KV,降重複 Prefill
  • 量化 KV(FP8/INT8):用精度換容量,需評估品質回歸
  • 限制 max_concurrency × max_context:最樸素也最有效的容量規劃
示意 · 單卡 HBM 切分(非精確比例)
Weights Activations / workspace KV Cache 可成長空間

該看的是最右邊那段:權重與 activation 是開機就固定的,只有 KV 那一段會隨「並發數 × 上下文長度」即時長大——容量規劃要規劃的就是這一段還剩多少。

陷阱只看「模型放不放得進卡」

7B 權重量化後可能輕鬆進 24GB,但 32k context × 高並發會把剩餘 HBM 吃光。容量規劃要以 權重 + 目標並發 × 目標上下文的 KV 一起算,而不是只看 checkpoint 大小。

一句話答法KV Cache 隨 seq_len 與並發成長,常比權重更早成為顯存瓶頸;分頁 KV、prefix cache 與並發上限是標準對策。
05

TTFT vs Throughput:怎麼取捨

互動產品護 TTFT;批次摘要/離線評測護吞吐;同一集群常用隔離與優先權拆開。

優化方向 常見手法 TTFT Throughput 風險
加大 continuous batch 提高 max_num_seqs / 佇列深度 變差 變好 長尾延遲、不公平
Prefill 優先/分離 獨立 prefill worker、chunked prefill 變好 可能略降 調度複雜、資源切分
縮短有效 prompt RAG top-k、摘要、prompt cache 變好 變好 品質/召回取捨(屬應用層)
投機解碼 / 小模型草稿 speculative decoding 視情況 常變好 接受率低時白算

沒有免費午餐:多數 serving 旋鈕是在「體感延遲」與「單位 GPU 產出」之間滑動。

實務上建議用產品 SLA 反推:聊天/客服把 TTFT p95 當硬約束,再在約束內最大化 tokens/s;離線評測或大量摘要任務則可接受較高 TTFT,換取更高 batch。必要時分池(interactive pool vs batch pool),比在單一佇列上調一個神奇參數更穩。

互動 · TTFT / Throughput 取捨掃描 拖旋鈕,看操作點在取捨軌跡上往哪邊滑

模型(誠實申報,教學示意非實測):一次 decode iteration 耗時 9 + 0.55 × b ms(固定項是整批共攤的權重讀取),prefill 吞吐 9000 token/s,但 prefill 只能吃 decode 沒用掉的那部分 GPU——batch 越大,prefill 能分到的比例越小。排隊延遲用 M/M/1 近似 ρ/(1−ρ)。KV 每 token 128 KiB(FP16)/64 KiB(FP8)。

取捨軌跡 · X=可服務吞吐 tokens/s,Y=TTFT ms(沿 batch 上限 b=1…32 掃描)
TTFT 對可服務吞吐的取捨軌跡,含 SLO 截線與當前操作點
當前操作點
拖動任一旋鈕即時重算。

看軌跡的最低點:batch 上限往右拉,可服務吞吐一路上升,但 TTFT 先降後升——降是因為排隊變短,升是因為 decode 吃走了 prefill 的預算。最低點右邊那一段,就是「用首 token 換吞吐」真正發生的地方。

一句話答法TTFT 與 throughput 常反向:加大 batch 抬吞吐但傷首 token;互動流量護 TTFT、批次流量護吞吐,必要時分池。
06

互動 Demo:Batching 與 KV 如何改寫延遲/吞吐

編好請求台帳與 GPU/KV 預算,逐 tick 推進排程器,看 slot 怎麼換人、KV 什麼時候觸頂。

互動 · Continuous Batching Scheduler 設好台帳與預算,按「下一 tick」逐 iteration 跑排程器

每按一次「下一 tick」就真的跑一次 iteration:先處理到達、再決定誰能 admit、再把該 tick 的計算預算分給 prefill 與 decode。規則(誠實申報,教學示意非實測):一個 tick = 20 ms、計算預算 512 unit;prefill 每 token 1 unit;decode 每 iteration 固定 160 unit(權重讀取,整批共攤)+每序列 6 unit。KV 每 token 128 KiB(=2 × 32 layers × 8 kv_heads × 128 head_dim × 2 B,FP16),故 1 GiB = 8192 token。Admission 以「已寫入 + 已承諾」的 KV 計;KV 觸頂而 decode 還要長時,最晚進來的那筆會被搶佔重算。

SLOT × TICK 佔用格帶(下緣橘線=該筆的首 token、灰線=EOS)
Prefill Decode 本 tick 沒分到預算 被搶佔重算 空 slot
KV 佔用 / 預算
請求類型(可點)prompt生成到達 tickTTFT完成 tick進度
按「下一 tick」開始;點台帳的「類型」欄可把任一筆在短問答 ↔ 長 RAG 之間切換。

該看的是格帶上「同一個 slot 換人」的那幾格:continuous batching 讓完成的請求立刻讓出 slot 給排隊者,而靜態 Batch 會讓整條格帶先空著等齊、再一起走——右側 KV 量規則負責告訴你,這場調度還有沒有記憶體可以調。

一句話答法Demo 要傳達的不是絕對數字,而是機制:靜態齊步走傷 TTFT;連續批處理抬吞吐;KV 空間不夠時再聰明的 scheduler 也只能拒單或換頁。
07

面試折衷與實務選型

把 serving 講成可落地的容量、SLA 與觀測問題,而不是背一串框架名稱。

面試常問 建議答法骨架
為什麼 LLM API 比一般 REST 難擴? 自回歸、狀態(KV)隨長度成長、串流長連線、延遲指標要拆 TTFT/TPOT。
如何提高 GPU 利用率? Continuous batching、paged KV、prefix cache;必要時 speculative decoding。
如何保證互動體驗? TTFT SLO、prefill 優先或分池、限制超長 context、背壓與排隊可見性。
顯存爆了怎麼辦? 降並發、縮 max_len、KV 量化、分頁換出、拒絕過長請求並回報可重試。
選型runtime 怎麼挑(高層)

先定義 SLA 與上下文分佈,再選 runtime/並行策略。開源棧常見路徑是「連續批處理 + 分頁 KV」;自建需補齊:佇列背壓、每請求取消、token 級計費、以及 Prefill/Decode 指標儀表板。框架名會變,這幾塊不會。

邊界本篇刻意不覆蓋

模型訓練、LoRA 熱更新、多模型路由、以及 Agent/Tool 編排都不在範圍。那些會改變「請求長什麼樣」,但不改變「GPU 上 Prefill → Decode → KV」這條物理約束。

一句話答法面試先講 Prefill/Decode、KV、continuous batching 與 TTFT/吞吐取捨;再補容量公式與分池,比堆砌產品名更有說服力。