LLM 的 Prompt 處理速度與文字生成速度都是看 GPU 有多快嗎?
Local LLM 工具常把速度分成 pp 與 tg(或 generation);這兩個數字衡量的是不同工作。
一次回答包含兩種不同工作
Section titled “一次回答包含兩種不同工作”LLM 推論開始後,大致會依序經過 Prefill 與 Decode。兩個階段使用同一個模型,處理資料的方式不同:
| 階段 | 做什麼? | 常見效能指標 |
|---|---|---|
Prefill(也稱 Prompt Processing、Context Processing,常縮寫為 pp 或寫成 PP) |
一次處理已知的輸入 Token,建立 KV Cache,準備第一個新 Token 所需的結果。 | Prompt Processing Token/s、Prefill Token/s;也會影響看到第一個輸出的等待時間。 |
Decode(也稱 Generation;llama.cpp 常縮寫為 tg) |
根據輸入與已產生的內容,依序計算下一個 Token,直到回答完成。 | Generation Token/s、Text Generation Token/s,以及兩個輸出 Token 之間的等待時間。 |
從送出 Prompt 到看到第一個輸出的時間常稱為 TTFT(Time to First Token)。Prefill 是 TTFT 的主要組成之一,但兩者不能直接畫上等號;模型載入、文字轉成 Token、排隊、資料傳輸與 Runtime 排程也可能算在 TTFT 內。
Decode 的互動速度則常用 TPOT(Time per Output Token) 或 Generation Token/s 表示。TPOT 越低,或 Generation Token/s 越高,畫面上的文字通常出現得越快。
Prefill 通常更受哪些因素影響?
Section titled “Prefill 通常更受哪些因素影響?”Prefill 已經知道整段輸入,因此可以把許多 Token 組成較大的矩陣運算,一起交給 GPU 處理。相較於單人 Decode,它通常更接近運算密集(compute-bound)的工作。
影響較明顯的因素包括:
- 輸入 Token 數: 貼入的文章、對話紀錄或檢索結果越長,需要處理的輸入通常越多。對使用完整注意力的 Transformer 而言,注意力運算量還會隨序列長度快速增加;輸入增加一倍,不保證 Prefill 時間只增加一倍。
- GPU 運算能力與利用率: Prefill 較有機會同時使用大量 GPU 運算單元,因此適合的矩陣乘法、注意力實作與資料精度都會影響速度。理論 TFLOPS 或 TOPS 仍不能直接換算成實際 Prompt Processing Token/s。
- Runtime 與核心最佳化: MLX、llama.cpp 或其他 Runtime 如何切分輸入批次、呼叫 GPU 核心與管理記憶體,可能讓相同硬體與模型出現不同結果。
- 模型規模、架構與精度: 較大的模型通常要完成更多運算;量化能減少資料量,但要有相符的高效率核心,才會轉化成實際加速。
Decode 通常更受哪些因素影響?
Section titled “Decode 通常更受哪些因素影響?”Decode 產生的下一個 Token 會成為再下一步的輸入。同一段回答中的未來 Token 無法全部預先知道,因此標準自回歸生成必須一步接著一步執行。
在單人、batch = 1 的常見本地使用情境中,每一步只新增一個 Token,卻仍要使用模型各層所需的權重。GPU 很難像 Prefill 一樣用大量已知 Token 填滿平行運算,因此搬移模型權重與 KV Cache 的時間容易成為主要限制。
影響較明顯的因素包括:
- 記憶體頻寬與權重大小: Decode 會反覆使用模型權重。相同 Runtime 與工作條件下,記憶體頻寬越高、每一步需要讀取的權重資料越少,通常越有機會提高生成速度。
- Context 長度與 KV Cache: Prompt 與已生成 Token 的中間結果會留在 KV Cache。Context 越長,Cache 占用與注意力需要處理的資料通常越多,可能降低 Decode 速度,也可能先遇到記憶體容量不足。
- 輸出 Token 數: 要求模型產生 1,000 個 Token,通常會比產生 100 個 Token 多執行約 900 個循序步驟。這會直接增加回答完成時間,即使 Generation Token/s 沒有明顯改變。
- 批次與同時請求數: 伺服器可以把多名使用者的 Decode 步驟組成批次,提高 GPU 利用率與整體吞吐量;單一使用者看到的 Token 間延遲卻可能因排程或競爭而增加。
- Runtime、硬體分工與取樣: GPU 核心、CPU/GPU 之間的資料搬移、部分權重卸載、取樣方法與 Runtime 排程都可能影響實際速度。
把兩個階段放在一起比較
Section titled “把兩個階段放在一起比較”下表預設模型已載入,而且是單人、單次請求、沒有多使用者批次的本地推論:
| 因素 | 對 Prefill 的典型影響 | 對 Decode 的典型影響 |
|---|---|---|
| 輸入長度 | 直接增加要處理的 Token 與注意力運算,影響第一個輸出的等待時間。 | 透過較大的 KV Cache 與較長 Context,影響後續每一步。 |
| 輸出長度 | 尚未進入 Decode,因此不會增加這次 Prefill 的輸入工作。 | 直接增加循序生成步驟與回答完成時間。 |
| GPU 運算能力 | 通常較容易成為主要影響因素。 | 仍會影響運算,但單人 Decode 常無法充分利用峰值算力。 |
| 記憶體頻寬 | 需要供應權重與資料,但不一定是主要瓶頸。 | 反覆讀取權重與 KV Cache,通常更容易成為主要瓶頸。 |
| 模型與量化版本 | 改變運算量、權重資料量與可用核心。 | 改變每一步要讀取及計算的資料量。 |
| Runtime 與批次設定 | 影響矩陣運算、注意力與輸入切分效率。 | 影響權重重用、KV Cache 管理、排程與 Token 間延遲。 |
這也是為什麼「統一記憶體越大,LLM 就一定越快」無法成立。容量先決定模型與 KV Cache 能不能放得下;模型載入後,Prefill 還要看 GPU 運算與 Runtime,Decode 則常更在意可持續使用的記憶體頻寬。完整的 Mac 容量與頻寬判斷可參考〈看懂統一記憶體與記憶體頻寬〉。
怎麼讀 pp 與 tg 的 Benchmark?
Section titled “怎麼讀 pp 與 tg 的 Benchmark?”llama.cpp 的 llama-bench 可以分開測量 Prompt Processing(pp)與 Text Generation(tg),也能測量兩者連續執行的結果。兩個項目都使用 Token/s,單位相同,工作內容不同,不能把數字大小直接當成同一種效能分數。
可以用以下近似關係理解一段回答花在哪裡:
- Prefill 時間約為「輸入 Token 數 ÷ Prompt Processing Token/s」。
- Decode 時間約為「輸出 Token 數 ÷ Generation Token/s」。
例如,2,000 個輸入 Token 以 1,000 Token/s 處理,Prefill 約需 2 秒;接著以 20 Token/s 產生 200 個 Token,Decode 約需 10 秒。這個簡化估算不含模型載入、文字轉成 Token、排隊、取樣與介面傳輸,卻能說明 pp 數字很高時,回答仍可能大部分時間花在 Decode。
比較兩台電腦、兩種 Runtime 或兩個模型版本時,至少要固定:
- 完整模型名稱、版本、量化與權重精度。
- Runtime 版本、運算後端與 CPU/GPU 分工設定。
- 輸入 Token、輸出 Token、Context 長度與批次設定。
- 是否包含模型載入、Prompt Cache、文字轉成 Token 與其他前後處理。
短問長答的聊天通常更在意 Decode;貼入長文件後要求簡短摘要,Prefill 與 TTFT 可能更重要。各用一組短 Prompt 與長 Prompt 重複測試,並分別記錄 pp、tg、TTFT 與記憶體占用,才能知道實際工作負載受到哪個階段限制。
閱讀後自我確認
Section titled “閱讀後自我確認”試著回答以下問題,來確認是否已學會。不確定答案的話,再回到對應段落複習。打勾狀態是方便你在這個頁面一一確認用,不會被儲存。