降低每個 Token 的生成成本
以搬移的位元組數估出 decode 時間下限,再檢驗量化、推測式解碼與注意力 kernel 實際省下什麼。
- 作者
- Huang Tzu LinFounder
- 發布日期
- 閱讀時間
- 約 41 分鐘
目錄
- 01本篇在學習路線中的位置
- 02開始之前:先備知識與學習成果
- 03你將建置的成品
- 04凍結模型與硬體
- 05為何 Decode 通常受記憶體頻寬限制
- 06權重量化
- 格式
- 為何離群值讓單純的 INT8 難以奏效
- GPTQ、AWQ 與 SmoothQuant
- 量化省下什麼,又省不下什麼
- 07完整範例:各格式的權重位元組與下限
- 08推測式解碼
- 先起草,再驗證
- 為何接受規則能保留目標分布
- 每次目標步驟的期望 Token 數
- 草稿成本與加速比
- 為何批次變大時效益縮小
- 變體:EAGLE 與 Medusa
- 推測式解碼對 ITL 的影響
- 09完整範例:一次推測式解碼試算
- 10注意力 Kernel
- FlashAttention:精確注意力,更少 HBM 往返
- FlashAttention-2 與 FlashAttention-3
- 為何 Kernel 對長 Prefill 最關鍵
- 11建立單一 Token 成本計算器
- 12刻意破壞的案例
- 「INT4 一定快 4 倍」
- 「推測式解碼會改變輸出」
- 「推測式解碼一定有幫助」
- 「FlashAttention 是近似注意力」
- 「量化對 Prefill 與 Decode 的加速一樣多」
- 13動手實作
- 14可量測的完成標準
- 15提取練習
- 16解答
- 17本文不涵蓋的內容
- 18來源與延伸閱讀
在小批次下,decode 步驟大部分時間都花在搬移位元組,而不是算術運算:為了替每條序列產生一個 token,它要從 GPU 記憶體讀出模型權重,以及每條活躍序列的 KV 快取。因此,降低單一 token 成本有三個著力點。量化讓每個權重少搬一些位元組,推測式解碼讓每讀過一輪權重就產出好幾個 token,FlashAttention 這類 IO 感知(IO-aware)的注意力 kernel 則減少注意力內部的記憶體流量。三者都有代價,也都會改變你該量測的項目。
本篇先讓單張 GPU 發揮效率,下一篇再把模型分散到多張 GPU。我們會為 700 億參數的範例模型推導 BF16、FP8 與 INT4 下 decode 時間的頻寬下限,用明確的接受率、草稿長度與草稿成本算出一個推測式解碼加速比,並指出每一種節省在什麼情況下不再適用。
估算單次 decode 步驟的成本下限
權重位元組變少能加快 decode,直到 KV 讀取或驗證運算成為瓶頸。
每個目標模型步驟的下限,ms
驗證只讀取同一份權重與 KV 一次;其矩陣運算(22.4 ms)仍低於記憶體時間(53.825 ms),論文公式的 2.40 倍仍成立。
每次迭代 75.35 ms,產出 1 到 5 個 token;平均每個 token 22.42 ms,一般步驟則是 53.82 ms。chunk 間隔拉長、平均 TPOT 下降,兩者都要回報。
讀懂目前結果
- 步驟下限:記憶體與矩陣運算
- 53.825 / 4.480 ms
- 每個目標步驟產出的 token
- 3.3616
- 加速:論文公式 → 計入驗證成本
- 2.40× → 2.40×
量化只縮小權重項,KV 讀取仍是 BF16。驗證的 batch × (γ + 1) 個位置一旦讓矩陣運算時間超過記憶體時間,論文分母中的「+ 1」就會變大,加速隨之下降。這裡的 α 是假設的平均值,不是實測接受率。
本文假設的 70B 模型下限:HBM 3.0 TB/s、稠密 BF16 1,000 TFLOP/s、BF16 KV、草稿成本 c = 0.1。實際 kernel 只會比下限慢。
本篇如何銜接前後內容
本篇在學習路線中的位置
帶入前作:拆解一次 LLM 推論請求:請求時間軸與指標計算器、管理 KV 快取:KV 區塊容量計算表
本篇新增:單一 token 成本計算表。請將假設、來源 ID 與失敗案例連同成果一併留存。
後續用途:將模型分配到多張 GPU 與量測推論容量將沿用這些成果。完整閱讀路線依序列出所有學習路徑,並標出來自其他路徑的先備課程。
學習目標與實作準備
開始之前:先備知識與學習成果
請先完成拆解一次 LLM 推論請求與管理 KV 快取。你應該已經清楚 TTFT、TPOT 與 ITL 的差異,會計算 GQA 模型每個 token 的 KV 位元組數,也知道 decode 為何通常受記憶體頻寬限制,而 prefill 通常受運算能力限制。
讀完本篇後,你將能夠:
根據權重位元組、KV 位元組、峰值頻寬與峰值運算能力,算出 decode 步驟時間的下限,並標明這是下限而非實測值;
說明 FP8 E4M3 與 E5M2、INT8 與僅權重 INT4 的差異,活化值的離群值為何讓單純的 INT8 量化難以奏效,以及 GPTQ、AWQ 與 SmoothQuant 各自改變了什麼;
區分量化能省下的部分(權重記憶體與搬移的位元組),以及它不會自動帶來的效果(更快的 prefill、缺少 kernel 支援時的加速,以及維持不變的品質);
說明推測式解碼的接受規則為何能保留目標模型的分布,並根據 α、γ 與草稿成本,算出每次目標步驟的期望 token 數與加速比;
解釋批次變大時推測式解碼的效益為何縮小,以及推測式解碼如何改變 ITL;以及
說明 FlashAttention、FlashAttention-2 與 FlashAttention-3 各自改變了什麼,以及它們為何對長 prefill 最關鍵。
你將建置的成品
你會產出 token-cost.json,內容包含:
範例模型、硬體假設,以及各格式的每權重位元數;
各格式的權重位元組數與 GiB 數;
各格式在批次 1 下、只計權重的頻寬下限;
每條序列帶 2,048 個 token 的 KV 時,批次 32 的 decode 步驟下限;
一組推測式解碼設定下,每次目標步驟的期望 token 數與加速比;以及
一項驗證成本檢查,指出在批次 32 下推測式解碼何時不再近乎免費。
成品中的每個時間值,都是從峰值硬體規格推得的下限。它告訴你哪些設定值得做基準測試,而不是預告基準測試的結果。
凍結模型與硬體
模型沿用〈拆解一次 LLM 推論請求〉的 80 層 GQA 模型,並採用〈將模型分配到多張 GPU〉的 700 億參數、隱藏維度 8,192 與模型版本 dense-70b-fixture-r1,讓數字可以跨篇沿用:
輸入 | 假設 |
|---|---|
模型 | 密集型 decoder-only Transformer,700 億參數,版本 |
層數/query head 數/KV head 數/head 維度 | 80/64/8(GQA)/128 |
KV 資料型別 | BF16,每個 token 327,680 位元組 |
Decode 快照 | 32 條活躍序列,每條各有 2,048 個 token 的 KV |
GPU 記憶體 | 80 GiB,其中 12 GiB 預留給執行期與 KV |
HBM 峰值頻寬 | 3.0 TB/s(十進位) |
密集 BF16 矩陣運算峰值吞吐量 | 1,000 TFLOP/s |
一般 decode 步驟 | 25 ms,即〈拆解一次 LLM 推論請求〉的合成 TPOT |
頻寬與運算兩列是假設性的整數值,不是任何產品規格。作為對照,NVIDIA 的 H100 SXM 產品頁列出 80 GB GPU 記憶體與 3.35 TB/s 記憶體頻寬;其 BF16 Tensor Core 數字 1,979 teraFLOPS 標註為「with sparsity」(啟用稀疏性),因此不是密集矩陣乘法的峰值(NVIDIA H100)。重複使用這份計算表之前,請把每一列都換成你自己規格表上的數值。
為何 Decode 通常受記憶體頻寬限制
每次前向傳播都受兩項成本限制。權重與 KV 快取存放在高頻寬記憶體(HBM),每次前向傳播都必須送到運算核心一次,Pope 等人稱之為記憶體時間(memory time);N 個參數的 decoder-only 模型每處理一個 token,還需要約 2N 次矩陣乘法 FLOPs,也就是每個參數一次乘法加一次加法(Pope et al., 2022)。兩者都完成之前,步驟不可能結束,所以兩者中較大的那個就是下限:
memory_time = (weight_bytes + batch × KV_bytes_per_sequence) / HBM_bandwidth
compute_time = 2 × parameters × positions_in_step / peak_FLOPs
step_time >= max(memory_time, compute_time)Pope 等人指出,對大型模型而言,注意力的矩陣乘法增加的 FLOPs 相對較少,通常可以不計入;但就記憶體而言,注意力仍然重要,因為每條序列都有自己的 KV 快取。在批次 1、BF16 下,光是權重就決定了下限:
weight bytes = 70,000,000,000 × 2 = 140,000,000,000 bytes
memory_time = 140 GB / 3.0 TB/s = 46.667 ms
compute_time = 2 × 70e9 × 1 / 1,000e12 = 0.140 ms
intensity = 140 GFLOP / 140 GB = 1 FLOP/byte
hardware ratio = 1,000 TFLOP/s / 3.0 TB/s ≈ 333 FLOP/byte運算單元每收到一個位元組,需要約 333 FLOPs 才能保持忙碌;批次 1 的 decode 只提供 1。批次處理能把權重位元組分攤到多條序列,卻分攤不了 KV 位元組,因為每條序列都帶著自己的快取。在 32 條序列的快照下:
KV per sequence = 327,680 × 2,048 = 671,088,640 bytes
memory_time = (140e9 + 32 × 671,088,640) / 3.0e12 = 53.825 ms
compute_time = 2 × 70e9 × 32 / 1,000e12 = 4.480 ms這個步驟仍然受記憶體頻寬限制,記憶體時間約為運算時間的 12 倍。每多一條 2,048-token 的序列,就多出 0.224 ms 的 KV 讀取,矩陣乘法時間卻只多 0.140 ms,所以在這個範例中,單靠加大批次永遠不會讓一般 decode 步驟變成受運算能力限制。Pope 等人也描述了同樣的轉變:小批次、短序列時,記憶體時間以載入權重為主;批次與序列變大後,則以載入 KV 為主(Pope et al., 2022)。
這些是下限,不是實測值。Chen 等人直接寫出只計權重的版本:模型大小除以所有晶片的總記憶體頻寬,就是自迴歸取樣速度的硬性上限(Chen et al., 2023)。這個下限假設每個位元組都以峰值頻寬搬移,關鍵路徑上也沒有其他工作,因此實測的步驟只會更慢。
權重量化
格式
量化以較少的位元儲存每個數值,再搭配縮放因子(scale),把低精度數值對應回原本的範圍。
FP8 有兩種編碼。E4M3(4 個指數位元、3 個尾數位元)的最大正規數值為 448,建議用於權重與活化值;E5M2 可達 57,344,建議用於梯度。數值在轉型前先乘上縮放因子,才能放進這麼窄的範圍(Micikevicius et al., 2022)。
INT8 以 8 位元整數儲存,縮放因子可以按張量、按通道或按向量設定;Micikevicius 等人指出,權重按通道、活化值按張量設定縮放因子,是 INT8 常見的做法。權重與活化值都採 INT8(W8A8)時,SmoothQuant 的作者指出,矩陣乘法這時可以改用 INT8 kernel,相較於 FP16,可讓 GPU 記憶體用量減半、矩陣乘法吞吐量接近加倍;他們自己量到的端對端結果是最高 1.56 倍加速,記憶體用量減為一半(Xiao et al., 2023)。
僅權重 INT4(W4A16)把權重存成 4 位元整數,每一組共用一個縮放因子;AWQ 全篇論文都採用 128 的分組大小。由於硬體沒有 INT4 × FP16 的乘法指令,kernel 會先把整數反量化為 FP16 再做矩陣運算,因此算術仍是 16 位元(Lin et al., 2024)。
為何離群值讓單純的 INT8 難以奏效
整個張量共用一個縮放因子時,縮放因子由必須表示的最大絕對值決定。Dettmers 等人發現,模型規模到約 67 億參數時,隱藏狀態中會出現數值大小最多達其他維度 20 倍的特徵,遍及所有 transformer 層,但在這個規模下只集中在 6 個特徵維度,一般的 8 位元方法也隨之失效。LLM.int8() 讓這些離群維度以 16 位元相乘,其餘超過 99.9% 的數值仍以 8 位元相乘,藉此維持 16 位元的準確度(Dettmers et al., 2022)。想看出問題所在,可以拿有 127 個正值刻度的對稱 INT8 格點為例:如果由一個大小為典型值 20 倍的特徵決定縮放因子,典型值只能落在約 127 / 20 ≈ 6 個刻度上。SmoothQuant 的作者這樣概括其中的不對稱:權重容易量化,活化值不容易(Xiao et al., 2023)。
GPTQ、AWQ 與 SmoothQuant
GPTQ 是一次性(one-shot)的訓練後權重量化方法,利用近似的二階資訊逐層選擇量化值。在作者的評估中,它能把權重壓到 3 或 4 位元,準確度損失可以忽略,量化 1,750 億參數的模型約需四個 GPU 小時(Frantar et al., 2023)。
AWQ 觀察到,只要保護約 1% 的顯著權重,就能大幅降低量化誤差,而且顯著通道應該依活化值的分布找出,而不是看權重大小。它在量化前以等價轉換放大這些通道,不需要反向傳播或重建(Lin et al., 2024)。
SmoothQuant 以 W8A8 為目標。它在離線階段把活化值除以按通道設定的平滑因子,再把這個因子併入權重;這是數學上等價的轉換,把量化難度從活化值轉移到權重,讓兩者都能使用 INT8 矩陣乘法(Xiao et al., 2023)。
量化省下什麼,又省不下什麼
低精度權重能減少一個副本所需的記憶體,以及每個 decode 步驟讀取的位元組,而後者正是上述下限中的主導項。GPTQ 的 decode kernel 說明了這個機制:它以量化矩陣乘上全精度向量,在運算當下即時反量化權重,不必量化活化值。反量化會多花一些運算,但 kernel 讀取的記憶體少得多,作者也把幾乎所有加速都歸功於這些 kernel(Frantar et al., 2023)。
量化不會自動省下的部分:
Prefill 運算。僅權重格式會在矩陣乘法前反量化為 16 位元,因此受運算能力限制的 prefill 仍在做 16 位元算術。SmoothQuant 的作者指出,僅權重方法靠減少資料載入來加快生成,在批次 1 時尤其明顯,W8A8 則可能更適合批次處理與上下文階段(Xiao et al., 2023);Pope 等人同樣指出,int8 權重能縮短載入權重的時間,在小批次時尤其有用(Pope et al., 2022)。
Kernel 支援。截至 2026 年 11 月,vLLM 的 FP8 W8A8 指南說明,FP8 運算需要 NVIDIA compute capability 8.9 以上(Ada Lovelace、Hopper、Blackwell),Turing 與 Ampere 則透過 Marlin kernel,以僅權重的 W8A16 方式執行 FP8 檢查點(vLLM FP8 W8A8);vLLM 的量化總覽頁另有按硬體列出的相容性表(vLLM Quantization)。Dettmers 等人也指出,對 67 億參數以下的模型,LLM.int8() 的額外開銷可能讓推論比 FP16 基準更慢(Dettmers et al., 2022)。
KV 位元組。權重量化不會改變 KV 快取本身的資料型別。KV 快取量化是另一項決策,有自己的準確度風險,管理 KV 快取已經討論過。
品質。每一種格式、分組大小與校準資料集,都會改變模型的數值行為。在採信已發表的準確度表格之前,請依照評估 AI 系統的做法,用你自己有版本編號的黃金測試集與迴歸閘門重新量測任務品質;vLLM 的 FP8 指南也把準確度評估列為量化流程的最後一步。
有一項已發表的結果,正好說明引用時必須一併保留哪些條件。GPTQ 在批次大小 1、生成 128-token 序列的條件下,量測 OPT-175B 每個 token 的平均延遲:3 位元模型在一張 A100 80GB 上為 71 ms,FP16 模型在五張 A100 80GB 上為 230 ms,延遲相差 3.24 倍(Frantar et al., 2023)。這項比較同時改變了精度與 GPU 數量,而且只涵蓋批次 1 的 decode。
完整範例:各格式的權重位元組與下限
GPTQ 估計分組大小為 128 時,每個權重約多出 0.15 位元(光是每 128 個權重配一個 16 位元縮放因子,就多 0.125 位元),所以計算表以每權重 4.15 位元計算 INT4。FP8 若每列 8,192 個權重配一個 32 位元縮放因子,每個權重增加不到 0.004 位元,計算表予以忽略。這裡假設全部 700 億個參數都經過量化;如果檢查點把嵌入層之類的層保留為較高精度,請按它們實際的位元寬度計算。
BF16 70e9 × 16 / 8 = 140.0000 GB = 130.39 GiB
FP8 E4M3 70e9 × 8 / 8 = 70.0000 GB = 65.19 GiB
INT4 g128 70e9 × 4.15 / 8 = 36.3125 GB = 33.82 GiB
batch-1 bound = weight_bytes / 3.0e12
batch-32 bound = (weight_bytes + 32 × 671,088,640) / 3.0e12格式 | 權重能否放進 68 GiB 可用空間? | 批次 1 權重下限 | 批次 32 步驟下限 | 批次 32 相對 BF16 的改善 |
|---|---|---|---|---|
BF16 | 否 | 46.667 ms | 53.825 ms | 1.00× |
FP8 E4M3 | 是 | 23.333 ms | 30.492 ms | 1.77× |
INT4,分組 128 | 是 | 12.104 ms | 19.262 ms | 2.79× |
有三點值得注意。第一,光是 BF16 權重(130.39 GiB)就超過一張 80 GiB 的 GPU,這也是下一篇要把權重切分到多張 GPU 的原因;FP8(65.19 GiB)放得進扣除 12 GiB 預留後剩下的 68 GiB,在計入量化中繼資料與活化值之前約有 2.8 GiB 餘裕。這一欄只計權重。批次 32 的快照還需要 32 × 640 MiB = 20 GiB 的 KV,超過 12 GiB 的預留空間,所以在一張 80 GiB 的 GPU 上只有 INT4 放得下(33.82 + 20 = 53.82 GiB);BF16 與 FP8 的批次 32 列應視為每一步的頻寬下限,而不是單張 GPU 的可行配置。第二,從 BF16 到 INT4 的位元比是 16 / 4.15 = 3.86×,批次 32 的下限卻只改善 2.79 倍,因為 21.47 GB 的 BF16 KV 讀取並沒有縮小。第三,若只計權重,要在批次 1 下以 FP8 達到 25 ms 的合成 TPOT,必須持續維持峰值頻寬的 23.333 / 25 = 93%;實際 kernel 能否做到,要靠量測回答。
推測式解碼
先起草,再驗證
推測式解碼讓目標模型搭配一個較便宜的草稿模型。草稿模型以自迴歸方式提出 γ 個 token,目標模型在一次平行前向傳播中替它們全部評分,接受其中一段前綴,並一定再補上一個自己產生的 token,因此每次目標前向傳播會產出 1 到 γ + 1 個 token(Leviathan et al., 2023)。這樣做划算,是因為 decode 受記憶體頻寬限制時,多評分幾個位置,權重與 KV 快取一樣只讀一次。Chen 等人逐一分析各元件:草稿很短時,線性層仍受記憶體頻寬限制;KV 快取不隨草稿長度增加;all-reduce 仍受延遲限制(Chen et al., 2023)。
為何接受規則能保留目標分布
以 p(x) 表示目標模型的下一個 token 分布,q(x) 表示草稿模型的分布,兩者都已套用 temperature、top-k 或 nucleus(top-p)取樣調整。對於從 q 抽出的草稿 token x:
accept x with probability min(1, p(x) / q(x))
on rejection, sample from p'(x) = max(0, p(x) - q(x)) / Σ max(0, p(x) - q(x))重新推導輸出為何服從 p。起草出 x 並接受它的機率是 q(x) × min(1, p(x) / q(x)) = min(p(x), q(x))。對所有 x 加總,接受的機率是 β = Σ min(p, q),所以拒絕的機率是 1 - β。由於 Σ max(0, p - q) = Σ (p - min(p, q)) = 1 - β,先拒絕再抽到 x 的機率是 (1 - β) × max(0, p(x) - q(x)) / (1 - β) = p(x) - min(p(x), q(x))。兩條路徑相加恰好是 p(x)。Leviathan 等人以 p 表示目標、q 表示草稿,證明了相同的結果;Chen 等人用的是同一套方法,只是兩個字母對調(Leviathan et al., 2023;Chen et al., 2023)。
分布相同不代表 token 相同。Chen 等人指出,數值計算與亂數種子處理的差異,意味著輸出不會完全一致,他們主張的是「在硬體數值精度範圍內」恢復目標分布(Chen et al., 2023)。截至 2026 年 11 月,vLLM 的指南稱其推測式取樣「在硬體數值的精度限制內,理論上無損」,並提醒批次大小的變化可能讓 logprobs 與輸出機率出現差異(vLLM Speculative Decoding)。
每次目標步驟的期望 Token 數
Leviathan 等人把 α 定義為目標模型接受一個草稿 token 的期望機率。假設每次接受彼此獨立且同分布,每次目標前向傳播產出的 token 數就服從截斷的幾何分布:
E[tokens per target step] = (1 - α^(γ+1)) / (1 - α)重新推導:每次前向傳播一定產出一個 token;目標模型接受第一個草稿 token 時(機率 α)產出第二個;前兩個都接受時(α²)產出第三個;依此類推,最多 γ + 1 個。期望值是 1 + α + α² + ... + α^γ,這個等比級數的和就是上式。
草稿成本與加速比
令 c 為草稿模型執行一次的時間除以目標模型執行一次的時間。一輪迭代的成本是 γc + 1 個目標步驟單位,論文給出的期望實際耗時改善為:
speedup = (1 - α^(γ+1)) / ((1 - α) × (γc + 1))這個定理建立在作者明言的假設上:γ + 1 次目標模型評估可以平行執行,而不增加實際耗時。只要目標步驟受記憶體頻寬限制、還有運算餘裕,這個假設就成立。論文也指出,這個結果假設生成長度夠長(Leviathan et al., 2023)。
為何批次變大時效益縮小
批次變大時,驗證前向傳播要處理批次 × (γ + 1) 個位置,運算餘裕隨之耗盡。EAGLE 的作者描述了這個機制,也實際量測:在 MT-bench、temperature 0 的條件下,他們回報 LLaMA2-Chat 70B 相對一般自迴歸解碼的加速比,從批次大小 1 的 3.01 倍,降到批次大小 2、3、4 的 2.81 倍、2.50 倍與 2.40 倍,同一節也說明 70B 的吞吐量測試是在四張 A100 40GB GPU 上以 FP16 執行(Li et al., 2024)。截至 2026 年 11 月,vLLM 的指南把推測式解碼定位為在中低 QPS、受記憶體頻寬限制的工作負載下降低 token 間延遲的方法(vLLM Speculative Decoding);TurboSpec 的作者在 vLLM 上實作時也觀察到,如果沒有針對實際流入的請求與推測方法調校就直接加入,推測式解碼可能降低推論服務效能(Liu et al., 2024)。
變體:EAGLE 與 Medusa
EAGLE 在特徵層級起草:一個小型 head 以目標模型倒數第二層的特徵做自迴歸,並輸入提前一步的 token 序列,再由目標模型以樹狀注意力(tree attention)驗證草稿樹,同時保留其輸出分布(Li et al., 2024)。
Medusa 在目標模型上加裝額外的解碼 head,以樹狀注意力一次驗證多個候選延續。Medusa-1 在凍結的骨幹模型上訓練這些 head,可做到無損加速;Medusa-2 連骨幹一起微調,等於改變了模型;可選用的典型接受(typical acceptance)機制則刻意不再與原始分布一致(Cai et al., 2024)。
推測式解碼對 ITL 的影響
現在 token 會一次抵達好幾個。每輪迭代最多產出 γ + 1 個 token,因此以 chunk 計的 ITL 會跟著迭代時間走,而迭代時間比一般 decode 步驟長;以 token 計的 TPOT 則會下降。量測推論容量說明了 chunk 間隔與每 token 平均值為何使用不同的分母,也把接受率與接受長度列為啟用推測式解碼時應記錄的訊號。絕對不要把啟用推測式解碼時量到的 chunk ITL 與一般 decode 的 ITL 當成同一種量測來比較。
完整範例:一次推測式解碼試算
取 α = 0.8、γ = 4、c = 0.1,也就是一次草稿步驟的成本是目標步驟的十分之一。這些是教學用的數值,不是任何模型組合的實測值。
E[tokens/step] = (1 - 0.8^5) / (1 - 0.8)
= (1 - 0.32768) / 0.2
= 3.3616 tokens per target step
speedup = 3.3616 / (4 × 0.1 + 1)
= 3.3616 / 1.4
= 2.4011×套用到〈拆解一次 LLM 推論請求〉中旅行 Copilot 的京都飯店請求,該篇合成軌跡中的一般 decode 步驟為 25 ms:
iteration time = 25 × (4 × 0.1 + 1) = 35 ms
average TPOT = 25 / 2.4011 = 10.41 ms/token
149 post-first-token outputs ≈ 149 × 10.41 ≈ 1,551 ms (versus 3,725 ms)平均值改善 2.40 倍,但串流 chunk 之間的間隔從 25 ms 拉長到 35 ms,每個 chunk 帶 1 到 5 個 token。α 是平均值,獨立性假設也只是建模上的簡化,所以敏感度比單一的代表數字更重要:α = 0.6 時,同樣的 γ 與 c 只得到每步 2.3056 個 token、1.65 倍;草稿較慢、c = 0.5 時,α = 0.8 只有 1.12 倍;α = 0.2 時,推測式解碼比一般 decode 還慢,只有 0.89 倍。
接著用下限模型檢驗「驗證免費」的假設。在 32 條序列的快照下,驗證要處理 32 × 5 = 160 個位置,運算時間為 2 × 70e9 × 160 / 1,000e12 = 22.4 ms。BF16(53.825 ms)與 FP8(30.492 ms)下,記憶體時間仍占主導,驗證依然近乎免費。INT4 下記憶體時間只有 19.262 ms,驗證因此變成受運算能力限制,需要 22.4 ms,約為一般步驟的 1.163 倍;把分母中的「+ 1」換成這個比例,加速比就從 2.40 倍降為 3.3616 / (0.4 + 1.163) = 2.15×。這是我們在 c 維持 0.1 的前提下對論文模型所做的延伸,不是論文本身的結果,但它點出一個真實的交互作用:權重位元組減少後,推測式解碼不再免費的批次大小也跟著降低。
注意力 Kernel
FlashAttention:精確注意力,更少 HBM 往返
標準注意力會把 N × N 的分數矩陣與機率矩陣寫入 HBM,再讀回來。FlashAttention 是 IO 感知的精確注意力演算法:它以分塊(tiling)方式把 Q、K、V 切成放得進晶片上 SRAM 的區塊,跨區塊逐步計算 softmax,並把各步驟融合成單一 kernel,讓 N × N 矩陣完全不必進出 HBM。論文證明其 HBM 存取次數為 O(N²d²/M),其中 d 為 head 維度、M 為 SRAM 大小,標準注意力則為 Ω(Nd + N²),並顯示在常見數值下存取次數最多只有標準注意力的 1/9(Dao et al., 2022)。同一篇論文中,只有區塊稀疏(block-sparse)延伸版本屬於近似計算。
FlashAttention-2 與 FlashAttention-3
FlashAttention-2 保留原本的演算法,重新分配工作:減少非矩陣乘法的 FLOPs,即使只有單一 head 也沿序列長度平行化,並改善 warp 之間的工作分配,相較 FlashAttention 約快 2 倍,在 A100 上達到理論最大 FLOPs/s 的 50%–73%(Dao, 2023)。FlashAttention-3 針對 Hopper:以 warp 特化(warp specialization)讓運算與資料搬移重疊,交錯執行區塊式矩陣乘法與 softmax,並以區塊量化搭配非相干處理(incoherent processing)讓 FP8 堪用。在 H100 80GB SXM5、FP16 輸入、序列長度 512 到 16k、每次執行總計 16k 個 token 的條件下,作者回報前向傳播比 FlashAttention-2 快 1.5–2.0 倍,最高 740 TFLOPs/s;FP8 路徑接近 1.2 PFLOPs/s,在作者含離群特徵的測試中,數值誤差只有逐張量量化標準 FP8 注意力的 1/2.6(Shah et al., 2024)。
為何 Kernel 對長 Prefill 最關鍵
分數矩陣中,每一組 query 位置與 key 位置的配對都對應一個元素。prefill 一次處理所有提示位置,所以若不分塊,32,768 個 token 的提示在每個 head 上都要建出 32,768 × 32,768 的分數矩陣,以 BF16 計為 2 GiB;decode 每條序列只有一個 query,分數列的長度就只等於上下文長度。這就是 FlashAttention 的節省主要出現在長 prefill 的原因。decode 的注意力則以讀取 KV 快取為主,Chen 等人指出,取樣時注意力機制的記憶體頻寬用量大多來自 KV 快取(Chen et al., 2023)。Flash-Decoding 的作者補充,FlashAttention 沿批次大小與 query 長度平行化,所以在批次大小 1、只有一個 query 時,用到的 GPU 不到 1%;Flash-Decoding 則把 key 與 value 切成多個小塊平行處理(Dao et al., 2023)。FlashAttention-3 的論文把針對 LLM 推論的最佳化列為未來工作(Shah et al., 2024)。請依工作負載中占主導的階段選擇 kernel,並針對推論引擎實際選用的注意力後端做基準測試。
建立單一 Token 成本計算器
存成 token_cost.py,再執行 python token_cost.py > token-cost.json:
import json
PARAMETERS = 70_000_000_000
LAYERS, KV_HEADS, HEAD_DIM, KV_BYTES_PER_ELEMENT = 80, 8, 128, 2
HBM_BYTES_PER_SECOND = 3.0e12
PEAK_DENSE_FLOPS = 1.0e15
CONTEXT_TOKENS, BATCH = 2048, 32
BITS_PER_WEIGHT = {"bf16": 16.0, "fp8_e4m3": 8.0, "int4_g128": 4.15}
ALPHA, GAMMA, DRAFT_COST = 0.8, 4, 0.1
PLAIN_STEP_MS = 25.0
if not (0 <= ALPHA < 1 and GAMMA >= 1 and DRAFT_COST >= 0 and BATCH >= 1):
raise ValueError("invalid speculation or batch setting")
kv_bytes_per_sequence = (2 * LAYERS * KV_HEADS * HEAD_DIM
* KV_BYTES_PER_ELEMENT * CONTEXT_TOKENS)
def step_bound_ms(weight_bytes, batch, positions_per_sequence):
memory = (weight_bytes + batch * kv_bytes_per_sequence) / HBM_BYTES_PER_SECOND
compute = 2 * PARAMETERS * batch * positions_per_sequence / PEAK_DENSE_FLOPS
return 1000 * max(memory, compute)
expected_tokens = (1 - ALPHA ** (GAMMA + 1)) / (1 - ALPHA)
speedup = expected_tokens / (GAMMA * DRAFT_COST + 1)
formats = {}
for name, bits in BITS_PER_WEIGHT.items():
weight_bytes = PARAMETERS * bits / 8
plain_ms = step_bound_ms(weight_bytes, BATCH, 1)
verify_ratio = step_bound_ms(weight_bytes, BATCH, GAMMA + 1) / plain_ms
formats[name] = {
"bits_per_weight": bits,
"weight_gb": weight_bytes / 1e9,
"weight_gib": weight_bytes / 2**30,
"batch1_weight_bound_ms": 1000 * weight_bytes / HBM_BYTES_PER_SECOND,
"batch_step_bound_ms": plain_ms,
"verify_cost_ratio": verify_ratio,
"speculative_speedup_at_batch": expected_tokens / (GAMMA * DRAFT_COST + verify_ratio),
}
artifact = {
"scope": "lower bounds from peak figures; not measurements",
"parameters": PARAMETERS,
"hbm_bytes_per_second": HBM_BYTES_PER_SECOND,
"peak_dense_flops": PEAK_DENSE_FLOPS,
"batch": BATCH,
"context_tokens": CONTEXT_TOKENS,
"kv_bytes_per_sequence": kv_bytes_per_sequence,
"formats": formats,
"speculation": {
"alpha": ALPHA,
"gamma": GAMMA,
"draft_cost": DRAFT_COST,
"expected_tokens_per_target_step": expected_tokens,
"speedup_free_verification": speedup,
"iteration_ms": PLAIN_STEP_MS * (GAMMA * DRAFT_COST + 1),
"average_tpot_ms": PLAIN_STEP_MS / speedup,
},
}
print(json.dumps(artifact, indent=2))預期的核心數值:
kv_bytes_per_sequence = 671088640
bf16: weight_gb = 140.0, batch1 ≈ 46.667 ms, batch32 ≈ 53.825 ms, verify_cost_ratio = 1.0
fp8_e4m3: weight_gb = 70.0, batch1 ≈ 23.333 ms, batch32 ≈ 30.492 ms, verify_cost_ratio = 1.0
int4_g128: weight_gb = 36.3125, batch1 ≈ 12.104 ms, batch32 ≈ 19.262 ms, verify_cost_ratio ≈ 1.1629
expected_tokens_per_target_step = 3.3616
speedup_free_verification ≈ 2.4011
int4_g128 speculative_speedup_at_batch ≈ 2.1509
iteration_ms = 35.0
average_tpot_ms ≈ 10.4117刻意破壞的案例
「INT4 一定快 4 倍」
4 位元對 16 位元的 4 倍比例,只適用於原始權重資料量。分組縮放因子讓 INT4 增加到約 4.15 位元(3.86 倍),批次 32 快照中 21.47 GB 的 BF16 KV 讀取並不會縮小(下限只改善 2.79 倍),而且下限也不是實測值。僅權重 INT4 下的 prefill 仍在跑 16 位元矩陣乘法,而在你的 GPU 上缺少快速 kernel 的格式,可能比 BF16 還慢。
「推測式解碼會改變輸出」
在拒絕取樣規則下,輸出分布在硬體數值精度範圍內與目標模型相同,前面已經重新推導過;不同的是你抽到哪一個樣本,因為亂數種子與數值計算不同。這個說法只對有損的變體成立:Medusa 的典型接受、像 Medusa-2 那樣微調過的骨幹,或 vLLM 的 FLy 驗證,vLLM 指南已標明後者不保留分布(vLLM Speculative Decoding)。
「推測式解碼一定有幫助」
在 α = 0.2、γ = 4、c = 0.1 時,每次目標步驟平均只產出 1.2496 個 token,迭代成本卻是 1.4 個目標步驟,所以推測式解碼的速度只有一般 decode 的 0.89 倍。在這組 γ 與 c 下,損益兩平點約在 α ≈ 0.287;批次變大時,「+ 1」的驗證成本也會增加,INT4 批次 32 的檢查已經顯示這一點。
「FlashAttention 是近似注意力」
FlashAttention 計算的是精確注意力,它靠避免 N × N 矩陣在 HBM 來回搬移來節省時間;只有同一篇論文中的區塊稀疏變體才是近似計算(Dao et al., 2022)。除了加總順序不同造成的微小浮點捨入差異,如果換了 kernel 之後品質改變,原因應該是精度路徑不同(例如 FP8 注意力)或程式錯誤,而不是分塊本身。
「量化對 Prefill 與 Decode 的加速一樣多」
小批次 decode 受位元組數限制,權重位元組變少就直接有幫助。prefill 通常受運算能力限制,而僅權重格式會在矩陣乘法前反量化為 16 位元,所以 prefill 的算術量不變,還多了反量化的工作。只有矩陣乘法本身以較低精度執行的格式,例如在支援的硬體上使用 FP8 或 INT8 W8A8,才可能減少 prefill 的運算,而且仍需要做品質檢查。
動手實作
擴充計算器,完成以下項目:
在 γ = 4、c = 0.1 下,讓 α 依序取 0.3、0.6、0.8 與 0.9;
在 α = 0.8、c = 0.1 下,找出 1 到 8 之間讓加速比最大的 γ;
以較慢的草稿模型(c = 0.5),重新計算 α = 0.8、γ = 4 的情況;
針對每種格式,找出在 2,048-token 上下文下,每條序列驗證 γ + 1 = 5 個位置時,讓下限變成受運算能力限制的最小批次;以及
以理想化的 1 位元組 FP8 KV(忽略 KV 縮放因子),重新計算 INT4 批次 32 的步驟下限。
推測式解碼各情況的參考值(皆假設驗證免費):
情況 | 每步期望 token 數 | 加速比 |
|---|---|---|
α 0.3、γ 4、c 0.1 | 1.4251 | 1.0179× |
α 0.6、γ 4、c 0.1 | 2.3056 | 1.6469× |
α 0.8、γ 4、c 0.1 | 3.3616 | 2.4011× |
α 0.9、γ 4、c 0.1 | 4.0951 | 2.9251× |
α 0.8、γ 6、c 0.1(最佳 γ) | 3.9514 | 2.4696× |
α 0.8、γ 4、c 0.5 | 3.3616 | 1.1205× |
BF16 在批次 98、FP8 在批次 49、INT4 在批次 26 時,驗證會變成受運算能力限制。改用 1 位元組 KV 後,INT4 批次 32 的步驟下限從 19.262 ms 降到 15.683 ms。請把這些記錄為下限模型的輸出,再寫出用來檢驗它們的基準測試計畫:以你自己的提示量得的接受率、在你的硬體上量得的草稿成本,以及實際批次大小下的 chunk ITL、TPOT 與有效吞吐量(goodput)。
可量測的完成標準
計算表符合以下條件才算通過:
模型、參數量、每權重位元數、KV 資料型別、批次大小、上下文長度、峰值頻寬與峰值運算能力都已明列,硬體數字標明為假設值或引用規格表;
BF16、FP8 與 4.15 位元 INT4 的權重位元組分別為 140.0、70.0 與 36.3125 GB;
批次 1 權重下限分別為 46.667、23.333 與 12.104 ms,批次 32 步驟下限分別為 53.825、30.492 與 19.262 ms;
每個時間值都標明為下限,而非實測值;
INT4 在批次 32 的改善記為下限改善 2.79 倍,而不是 4 倍;
α = 0.8、γ = 4、c = 0.1 得出每次目標步驟 3.3616 個期望 token 與 2.4011 倍加速比,並寫明獨立性與驗證免費兩項假設;
INT4 批次 32 的驗證比例(1.163)與調整後加速比(2.15 倍)標明為對論文模型的延伸;
推測式解碼的執行結果分別回報接受率、chunk ITL 與 TPOT;
每一種量化設定都附上你自己評估集的品質結果;以及
每個引用的論文加速數字,都寫明其模型、硬體、工作負載、比較基準與指標。
提取練習
在範例模型上,批次 1 的 BF16 decode 步驟為何至少要 46.667 ms,即使它的矩陣乘法在峰值下只需 0.140 ms?
批次處理為何能分攤權重讀取,卻分攤不了 KV 讀取?
活化值的離群值為何讓按張量設定的 INT8 難以奏效?SmoothQuant 又如何因應?
僅權重 INT4 為何對 decode 的幫助大於 prefill?
證明推測式解碼的接受規則產出 token x 的機率是 p(x)。
推測式解碼為何能降低 TPOT,同時拉長 chunk ITL?它的效益為何在大批次時縮小?
FlashAttention 省下了什麼?為何這對 32k-token 的 prefill 比對 decode 更重要?
解答
每個步驟都必須從 HBM 讀出全部 140 GB 的 BF16 權重,在峰值下需要
140 GB / 3.0 TB/s = 46.667 ms,而2 × 70e9次 FLOPs 在 1,000 TFLOP/s 下只需 0.140 ms;這個步驟每個位元組只提供 1 FLOP,硬體比例卻約為 333。同一批次中的所有序列共用同一份權重,每個步驟只讀一次;但每條序列的 KV 快取各自獨立,必須分別讀取。在 2,048 個 token 下,每條序列每步多出 671,088,640 位元組,也就是 0.224 ms。
整個張量共用的縮放因子由最大絕對值決定,所以一個大小為典型值 20 倍的特徵,會讓典型值只剩下 INT8 的 127 個正值刻度中約 6 個。SmoothQuant 把活化值除以按通道設定的平滑因子,再把因子併入權重,以等價轉換讓兩者都能量化為 INT8。
小批次 decode 受搬移的位元組數限制,而僅權重 INT4 正好減少這部分;prefill 通常受運算能力限制,W4A16 kernel 又會在矩陣乘法前把權重反量化為 16 位元,所以算術量不會縮小。
起草出 x 並接受的機率是
q(x) × min(1, p(x) / q(x)) = min(p(x), q(x));拒絕的機率是1 - β,其中β = Σ min(p, q);接著從殘差分布抽樣,貢獻p(x) - min(p(x), q(x));兩條路徑相加為 p(x)。每輪迭代的成本是
γc + 1個一般步驟(範例中為 35 ms),平均卻產出 3.3616 個 token,因此 TPOT 降到 10.41 ms,串流 chunk 之間的間隔則拉長到 35 ms。批次變大時,批次 × (γ + 1) 個驗證位置會耗盡公式所假設的運算餘裕,驗證成本因此超過一個一般步驟,加速比隨之縮小。它以分塊方式在晶片上 SRAM 中計算,避免把 N × N 分數矩陣寫入 HBM 再讀回,同時仍計算精確注意力。32k-token 的 prefill 若不這麼做,每個 head 都要建出 32,768 × 32,768 的矩陣(BF16 下為 2 GiB);decode 每條序列只有一列 query,主要成本反而是讀取 KV。
本文不涵蓋的內容
將模型分配到多張 GPU 會切分單張 GPU 放不下的 BF16 模型,並加入張量平行化在每個步驟帶來的集體通訊成本。〈量測推論容量〉在負載下量測 TTFT、ITL、TPOT 與有效吞吐量,本篇的每個下限都要在那裡驗證。KV 快取量化屬於〈管理 KV 快取〉,品質閘門屬於〈評估 AI 系統〉。剪枝、蒸餾與混合專家模型的稀疏化不在本篇範圍內。
來源與延伸閱讀
Pope, R., Douglas, S., Chowdhery, A., et al. "Efficiently Scaling Transformer Inference." arXiv:2211.05102, 2022。記憶體時間與運算時間成本模型、每個 token 約 2N 次矩陣乘法 FLOPs 的近似,以及 KV 載入隨批次大小增加的第一手論文。arxiv.org/abs/2211.05102
NVIDIA. "NVIDIA H100 Tensor Core GPU." 產品頁面,查閱日期 2026-11-03。記憶體容量、記憶體頻寬與標註稀疏運算之 Tensor Core 數字的第一方資料。nvidia.com
Micikevicius, P., Stosic, D., Burgess, N., et al. "FP8 Formats for Deep Learning." arXiv:2209.05433, 2022。E4M3 與 E5M2 編碼、其數值範圍與建議用途,以及縮放因子的第一手論文。arxiv.org/abs/2209.05433
Dettmers, T., Lewis, M., Belkada, Y., and Zettlemoyer, L. "LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale." NeurIPS, 2022。湧現的活化值離群值與混合精度分解的第一手論文。arxiv.org/abs/2208.07339
Frantar, E., Ashkboos, S., Hoefler, T., and Alistarh, D. "GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers." ICLR, 2023。一次性二階權重量化、分組大小額外開銷與批次 1 decode kernel 結果的第一手論文。arxiv.org/abs/2210.17323
Lin, J., Tang, J., Tang, H., et al. "AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration." MLSys, 2024。依活化值找出顯著通道並加以放大、分組大小 128 與 W4A16 反量化的第一手論文。arxiv.org/abs/2306.00978
Xiao, G., Lin, J., Seznec, M., et al. "SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models." ICML, 2023。W8A8 平滑轉換,以及僅權重與 W8A8 量化取捨的第一手論文。arxiv.org/abs/2211.10438
vLLM Project. "Quantization." 現行文件,查閱日期 2026-11-03。支援的量化方法清單與按硬體列出之相容性表的官方說明。docs.vllm.ai
vLLM Project. "FP8 W8A8." 現行文件,查閱日期 2026-11-03。FP8 的 compute capability 需求、僅權重 Marlin 備援路徑,以及量化後準確度評估的官方說明。docs.vllm.ai
Leviathan, Y., Kalman, M., and Matias, Y. "Fast Inference from Transformers via Speculative Decoding." ICML, 2023。接受規則、每次目標步驟期望 token 數與實際耗時改善公式的第一手論文。arxiv.org/abs/2211.17192
Chen, C., Borgeaud, S., Irving, G., et al. "Accelerating Large Language Model Decoding with Speculative Sampling." arXiv:2302.01318, 2023。修正版拒絕取樣、記憶體頻寬上限,以及評分短草稿為何只需約一個步驟時間的第一手論文。arxiv.org/abs/2302.01318
Li, Y., Wei, F., Zhang, C., and Zhang, H. "EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty." arXiv:2401.15077, 2024。特徵層級起草,以及加速比隨批次大小變化的第一手論文。arxiv.org/abs/2401.15077
Cai, T., Li, Y., Geng, Z., et al. "Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads." ICML, 2024。解碼 head、樹狀注意力與典型接受機制的第一手論文。arxiv.org/abs/2401.10774
Liu, X., Park, J., Hu, L., et al. "TurboSpec: Closed-loop Speculation Control System for Optimizing LLM Serving Goodput." arXiv:2406.14066, 2024,2025 年修訂。說明在 vLLM 中未經調校的推測式解碼會因工作負載而降低效能的第一手論文。arxiv.org/abs/2406.14066
vLLM Project. "Speculative Decoding." 現行文件,查閱日期 2026-11-03。適用範圍、數值精度注意事項與支援之推測方法的官方說明。docs.vllm.ai
Dao, T., Fu, D. Y., Ermon, S., et al. "FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness." arXiv:2205.14135, 2022。分塊、IO 感知的精確注意力及其 HBM 存取分析的第一手論文。arxiv.org/abs/2205.14135
Dao, T. "FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning." arXiv:2307.08691, 2023。FlashAttention-2 工作分配改進與 A100 使用率的第一手論文。arxiv.org/abs/2307.08691
Shah, J., Bikshandi, G., Zhang, Y., et al. "FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision." arXiv:2407.08608, 2024。Hopper 非同步執行、FP8 注意力與 H100 基準測試條件的第一手論文。arxiv.org/abs/2407.08608
Dao, T., Haziza, D., Massa, F., and Sizov, G. "Flash-Decoding for Long-Context Inference." Stanford CRFM, 2023-10。作者說明在 decode 注意力中切分 key 與 value 的技術文章。crfm.stanford.edu
閱讀順序
完整閱讀路線
第 6.4 課
Huang Tzu Lin
富宇資訊科技創辦人。撰寫 AI 系統設計、證據與工程取捨的文章。
