iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Software Development

LLM infra 學習日記系列 第 16

Chunked Prefill:讓長 Prompt 不要卡住正在生成的 Request

  • 分享至 

  • xImage
  •  

Day 15 的 Continuous Batching 解決了「完成一條,就補進一條」。

但還有另一種卡頓:一條很長的新 prompt 進來,Prefill 一次要處理幾千個 tokens;正在 decode 的 requests 雖然只各需要一個 token,卻得等這個長 step 結束。

Chunked Prefill 的想法很單純:不要一次跑完整條 prompt,把它切成多個 scheduler steps。

先說清楚:今天是概念與 source tracing,沒有執行 benchmark。


為什麼長 Prefill 會傷到 Decode?

Prefill 會平行處理 prompt 裡的所有 tokens,通常是 compute-heavy;Decode 則每條 request 一次只前進一個 token。

當一條長 prompt 和許多正在串流輸出的 requests 共用 GPU 時,如果 Scheduler 把完整 prompt 一次排進去:

長 Prefill step 變很久
→ Decode requests 這一步無法前進
→ 使用者看到下一個 token 的間隔變長

Chunked Prefill 會把 prompt 切成連續片段:

P[0:chunk] → P[chunk:2×chunk] → P[2×chunk:3×chunk] → ...

每一段算完後,KV Cache 都保留下來。下一段能直接讀取已算好的 prefix,不是從頭重算。


一個紙上 trace

假設每個 step 的 token budget 是 16。目前有六條 decode requests,各自需要一個 token;新來的 P 有 40-token prompt。

Step Decode 給 P 的 Prefill chunk P 已完成 Prefill
1 6 tokens P[1:10] 10 / 40
2 6 tokens P[11:20] 20 / 40
3 6 tokens P[21:30] 30 / 40
4 6 tokens P[31:40] 40 / 40

這個排程選擇先讓六條 decode 都前進,再把剩下的十個 token 用在 PP 完成最後一段 Prefill 後,就能進入正常的 generation。

這個表只是在追 token budget,不代表四個 steps 的實際時間相同,也不是 benchmark。

如果沒有 chunking,單一 iteration 的 token 上限小於 prompt 長度時,系統甚至無法在該上限內完整排入這條 prompt。vLLM 的設定檢查也明確指出:沒有 Chunked Prefill 時,過小的 max_num_batched_tokens 會限制可接受的最大 sequence 長度。


在 vLLM V1 裡實際在切什麼?

V1 Scheduler 為每條 request 記錄已計算的 tokens。一次排程時,它先算出還欠多少:

num_new_tokens
= 尚未計算的 prompt / output tokens

接著把這個數字限制在本 step 的可用預算內:

可排入 tokens
= min(還欠的 tokens, 剩餘 token budget, 其他限制)

若設定了 long_prefill_token_threshold,它還能為長 prompt 加上每條 request 的上限。Scheduler 會為這一段配置 KV Cache slots,讓 request 留在 running 狀態;下一輪從新的 num_computed_tokens 繼續。

所以 Chunked Prefill 切的不是字串,也不是把模型重跑好幾遍;切的是每一輪要執行的 token work

vLLM V1 的 unified scheduler 用 {request_id: num_tokens} 表示每個 request 在這個 step 的工作量。這讓 partial prefill 和 decode 可以共享同一個 token budget。


三個會一起影響行為的設定

設定 它限制什麼 拉高後的直覺
max_num_batched_tokens 單一 iteration 可處理的總 token 數 Prefill chunk 可更大,但單一步驟也可能更久
long_prefill_token_threshold 長 prompt 單條 request 的 cap;0 不啟用此 cap 更大代表更少 chunks;更小代表更常讓出預算
max_num_seqs 單一 iteration 可容納的 sequences 數 可同時維持更多 decode requests,但仍受 KV Cache 限制

沒有一組數字在所有 workload 都最好。短 prompt、長 context、輸出很長、低併發與高併發,會導向不同選擇。


這是在交換什麼?

較大的 chunk
→ 長 prompt 較快完成 Prefill
→ 該 request 的 TTFT 可能更好
→ 其他 requests 的 inter-token latency 可能變差

較小的 chunk
→ Decode 較不容易被長 Prefill 卡住
→ tail ITL 通常較容易受保護
→ 長 prompt 要經過更多 scheduler steps,TTFT 可能變差

因此 Chunked Prefill 不是「一定更快」,而是把原本一大段不可中斷的 Prefill work,變成 Scheduler 可以穿插安排的 work units。

Sarathi/Sarathi-Serve 將這個觀點做得更明確:在維持 decode 前進的同時,讓 prefill chunks 填入剩餘工作量。


今天的結論

Continuous Batching:哪條 request 可以進入下一個 batch?
Chunked Prefill:一條長 request 在這個 batch 可以吃多少 tokens?

兩者合在一起,Scheduler 才能面對真實流量:新 prompt 不斷到達,舊 request 又必須持續 streaming。

下一篇看另一個減少 Prefill 的方法:如果兩個 requests 前面一段 prompt 相同,Prefix Caching 能不能連 Prefill 都少算?

Reference


上一篇
Continuous Batching:Batch 為什麼可以中途換人?
下一篇
Prefix Caching:相同 Prompt 為什麼不用再算一次?
系列文
LLM infra 學習日記19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言