JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

兩個階段、一道牆:大型語言模型推論的解剖學

在加速推論之前,你得先知道它究竟在做什麼。認識 prefill、decode、記憶體頻寬之牆,以及其他一切都在優化的延遲指標。

為何服務本身就是一門學問

你在概念層次上已經理解 訓練與推論 的差別:訓練調整權重,推論只跑前向傳播。因此很容易以為推論是簡單的那一半。對 大型語言模型(LLM)而言,事實恰好相反。一次訓練是攤提到數個月的一次性成本;推論卻是你在每個請求的每個 token、永遠都要付出的成本。壓低這個成本,正是今日應用人工智慧工程的一大部分,它有自己的詞彙、自己的瓶頸,以及自己出人意料的失敗模式。

LLM 推論之所以困難,並非因為算力不足——現代 GPU 的算術吞吐量極為龐大。困難在於自迴歸生成是循序的,而且受記憶體限制。要產生第 200 個 token,你必須先產生第 199 個,而且每一步都為了極少量的計算而讀取大量的狀態。本篇建立的心智模型,是本軌道其餘篇章所仰賴的基礎。

Prefill 與 decode:兩種非常不同的工作

從 Transformer 生成可分成兩個階段。Prefill(預填) 一次處理整段提示:每個提示 token 都注意它前面所有的 token,因此整段提示在一次高度平行、以矩陣運算為主的傳遞中流過網路。Decode(解碼) 接著透過 自迴歸解碼 一次吐出一個輸出 token:每個新 token 都需要對單一位置做一次前向傳播,並回頭注意先前所有的 token。

解码阶段就是这样逐个 token 自回归地生成——这正是让推理变难的顺序瓶颈。

自回归生成循环示意图,每个输出 token 被反馈作为下一步的输入。

KV 快取,以及 decode 為何讀取如此之多

若天真地生成第 t 個 token,會重新計算前面 *t-1* 個 token 的鍵與值再做注意,使生成變成平方級。我們改為把每一層的鍵與值存進 KV 快取 並重複使用:在第 t 步只計算新 token 的查詢(query)、鍵(key)、值(value),把後兩者附加到快取再做注意。這把每步的計算從平方級降為線性——卻把問題轉移到記憶體。快取隨序列長度增長,而且每一個 decode 步都得整份讀取。

KV 缓存正是把每一层的键和值存下来,让解码无需重新计算。

注意力作为软查询—键—值查找的示意图,带 softmax 权重。

# Decode step, conceptually
q, k, v = project(x_t)          # one token's worth
kv_cache.k.append(k)            # grow the cache
kv_cache.v.append(v)
attn = softmax(q @ kv_cache.k.T) @ kv_cache.v   # read the WHOLE cache
x_next = mlp(attn)
每個 decode 步只做極少運算(一個查詢),卻要讀取整份快取與所有權重——這正是受記憶體限制的本質。

記憶體頻寬之牆

一個有用的數字是算術強度(arithmetic intensity):每從記憶體讀取一個位元組所執行的浮點運算數。Decode 的算術強度極低——批次大小為一時,你讀取數十億位元組的權重卻只做數十億次 FLOP,於是運算遠在資料抵達之前就算完了。這個核心是 受記憶體頻寬限制(memory-bandwidth-bound) 的,GPU 的張量核心多半閒置。形式化的圖像見 算術強度

\text{Arithmetic intensity} = \frac{\text{FLOPs performed}}{\text{bytes moved from memory}}

算术强度(每搬运一字节对应的浮点运算数)是解释解码瓶颈的唯一关键数字。

這一個事實就解釋了整個領域。批次處理把每次權重讀取攤提到許多請求上,提高算術強度。KV 快取壓縮分頁減少你必須搬動的位元組。推測解碼在每一趟記憶體存取中做更多有用的運算。量化讓每個位元組承載更多資訊。把這道牆放在心上,之後每一招的動機就一目了然。

一切優化所追求的指標

服務有兩個延遲指標,由 TTFT 與 TPOT 刻畫。首 token 時間(time to first token, TTFT) 主要受 prefill 主導,量測使用者在任何東西出現前要等多久。每輸出 token 時間(time per output token, TPOT),有時稱為 token 間延遲,量測串流開始後穩態的 decode 速度。這些是面向使用者的服務水準目標(SLO),且與吞吐量相互權衡。

T_{\text{total}} = \text{TTFT} + (N - 1)\cdot \text{TPOT}

端到端延迟可分解为由预填充主导的 TTFT,加上每个后续生成 token 的一次 TPOT。

永恆的張力是 延遲與吞吐量。較大的批次更有效率地使用 GPU,提高所有使用者的每秒 token 數(吞吐量,這驅動了 推論成本),卻可能損害單一使用者的 TPOT。一個服務系統本質上就是個排程器,要在達成延遲 SLO 的同時,讓昂貴的硬體保持忙碌。