跳到內容

LLM 的 Prompt 處理速度與文字生成速度都是看 GPU 有多快嗎?

Local LLM 工具常把速度分成 pptg(或 generation);這兩個數字衡量的是不同工作。

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 已經知道整段輸入,因此可以把許多 Token 組成較大的矩陣運算,一起交給 GPU 處理。相較於單人 Decode,它通常更接近運算密集(compute-bound)的工作。

影響較明顯的因素包括:

  • 輸入 Token 數: 貼入的文章、對話紀錄或檢索結果越長,需要處理的輸入通常越多。對使用完整注意力的 Transformer 而言,注意力運算量還會隨序列長度快速增加;輸入增加一倍,不保證 Prefill 時間只增加一倍。
  • GPU 運算能力與利用率: Prefill 較有機會同時使用大量 GPU 運算單元,因此適合的矩陣乘法、注意力實作與資料精度都會影響速度。理論 TFLOPSTOPS 仍不能直接換算成實際 Prompt Processing Token/s。
  • Runtime 與核心最佳化: MLX、llama.cpp 或其他 Runtime 如何切分輸入批次、呼叫 GPU 核心與管理記憶體,可能讓相同硬體與模型出現不同結果。
  • 模型規模、架構與精度: 較大的模型通常要完成更多運算;量化能減少資料量,但要有相符的高效率核心,才會轉化成實際加速。

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 排程都可能影響實際速度。

下表預設模型已載入,而且是單人、單次請求、沒有多使用者批次的本地推論:

因素 對 Prefill 的典型影響 對 Decode 的典型影響
輸入長度 直接增加要處理的 Token 與注意力運算,影響第一個輸出的等待時間。 透過較大的 KV Cache 與較長 Context,影響後續每一步。
輸出長度 尚未進入 Decode,因此不會增加這次 Prefill 的輸入工作。 直接增加循序生成步驟與回答完成時間。
GPU 運算能力 通常較容易成為主要影響因素。 仍會影響運算,但單人 Decode 常無法充分利用峰值算力。
記憶體頻寬 需要供應權重與資料,但不一定是主要瓶頸。 反覆讀取權重與 KV Cache,通常更容易成為主要瓶頸。
模型與量化版本 改變運算量、權重資料量與可用核心。 改變每一步要讀取及計算的資料量。
Runtime 與批次設定 影響矩陣運算、注意力與輸入切分效率。 影響權重重用、KV Cache 管理、排程與 Token 間延遲。

這也是為什麼「統一記憶體越大,LLM 就一定越快」無法成立。容量先決定模型與 KV Cache 能不能放得下;模型載入後,Prefill 還要看 GPU 運算與 Runtime,Decode 則常更在意可持續使用的記憶體頻寬。完整的 Mac 容量與頻寬判斷可參考〈看懂統一記憶體與記憶體頻寬〉

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 或兩個模型版本時,至少要固定:

  1. 完整模型名稱、版本、量化與權重精度。
  2. Runtime 版本、運算後端與 CPU/GPU 分工設定。
  3. 輸入 Token、輸出 Token、Context 長度與批次設定。
  4. 是否包含模型載入、Prompt Cache、文字轉成 Token 與其他前後處理。

短問長答的聊天通常更在意 Decode;貼入長文件後要求簡短摘要,Prefill 與 TTFT 可能更重要。各用一組短 Prompt 與長 Prompt 重複測試,並分別記錄 pptg、TTFT 與記憶體占用,才能知道實際工作負載受到哪個階段限制。

試著回答以下問題,來確認是否已學會。不確定答案的話,再回到對應段落複習。打勾狀態是方便你在這個頁面一一確認用,不會被儲存。

閱讀後自我確認