只用了 8 格,
為什麼占了 12 格?
星期五晚上,車站的置物櫃區同時來了六位旅客。每個人的行李都會越放越多,而且沒有人知道自己會待多久。老管理員的辦法是每人先鎖 12 格連號;新來的管理員阿頁說:不必連號,我有一張對照表。同樣 60 格櫃子,能進來的人數就不一樣了。
故事裡會出現的東西
- 客旅客一位旅客就是一段正在進行的對話(一個請求)。
- 李行李對話留下的名牌和資料夾(KV Cache),越聊越多。
- 櫃置物櫃60 格,就是顯示卡上留給 KV Cache 的記憶體。
- 老老管理員每人先鎖 12 格連號,怕你之後放不下。
- 頁阿頁新管理員,一格一格給,靠對照表記位置。
- 窗服務窗口3 個,一輪能同時處理 3 位旅客。
行李越放越多,而且沒人知道你會待多久
先弄清楚:為什麼記憶體會被「對話」吃掉
先接上前兩集。模型每處理一個片段,都會在每層樓留下一對名牌和資料夾(K 和 V),讓後面的片段回頭比對用。這些東西不能丟,因為只要對話還在進行,下一個字就會用到它們。你可以把它們想成旅客的行李:每多聊一句,行李就多幾件,而且要放在櫃子裡隨時能拿。
置物櫃就是顯示卡上留給這些行李的記憶體。它有兩個特性讓管理變得棘手。第一,行李會一直變多,對話沒結束就不會停。第二,沒有人事先知道會多到哪裡:有的人問一句就走,有的人聊了半小時還在補條件。管理員收下第一件行李時,只能猜。
一位旅客的行李是怎麼變多的
行李增加還有一個特別的性質:它只會往後面加,不會中途插進去,也不會拿掉前面的。所以管理員最自然的想法是:給每個人一整排連號的格子,新行李放到下一格就好,讀取時也從第一格一路讀到最後一格,不用東找西找。問題是,那一排要留多長?
回到電腦裡
行李就是 KV Cache:每個 token 在每一層的 K 和 V 向量。它的大小隨對話長度線性增加,對話越長、越多人同時用,占的顯示記憶體就越多;在多人服務的情境下,它常常是比模型權重更難管的那一部分。一格「頁」(block)可以放固定數量的 token,例如 16 個。傾向連號存放的原因是注意力運算讀取 K/V 時,連續的記憶體位址最單純、最快。
老管理員的辦法:每人先鎖 12 格連號
方便是方便,代價在人多的時候才看得見
老管理員的規則很簡單:不管你來的時候帶幾件,一律先鎖 12 格連號給你,因為 12 格是這座車站允許的最大行李量。這樣你之後怎麼加都放得下,讀取也永遠是連續的。他不用猜,也不用中途搬家。
代價藏在沒被用到的格子裡。你今天只帶了 8 件,剩下 4 格空著,但它們已經是你的,別人不能用。這 4 格不是「空閒」,是「沒有東西卻被占著」。
同一位旅客,8 件行李,兩位管理員各怎麼安排
老老管理員:先鎖 12 格連號
行李放前面,後面的格子鎖著等你。
頁阿頁:有幾件給幾格
剩下的格子誰都可以用。
一個人少占 4 格,聽起來沒什麼。可是置物櫃區不是只有一個人在用。星期五晚上,六位旅客同時到了。
同樣 60 格,哪一位管理員能讓更多人進來
兩邊收到完全相同的旅客,依 A、B、C… 的順序入場。整份行李放得下才讓人進,放不下的在等待區,不會硬塞。
老老管理員
每人一到就鎖 12 格連號。
頁阿頁
照目前的行李量給格子。
請試試「行李總量超過 60 格」:當每個人真的都帶了一大堆,阿頁也一樣要讓人等。她沒有把行李變小,她省下的只是那些沒有行李卻被鎖住的格子。資料真的需要多少空間,就還是需要多少。
回到電腦裡
老管理員的做法是依最大可能長度預先配置連續記憶體。沒用到的部分叫內部碎片(internal fragmentation);此外,不同長度的區段在記憶體裡進進出出,還會留下許多不連續的小空隙,明明加起來夠大卻塞不進一個新請求,這叫外部碎片。PagedAttention 的論文估計,這類浪費在某些既有系統裡讓 KV Cache 記憶體的實際利用率只剩兩、三成。這裡的示範只畫預留造成的內部碎片,並假設一格剛好裝滿。
阿頁的辦法:不必連號,我有一張對照表
PagedAttention 到底改了什麼
老管理員堅持連號,是因為他相信讀取時必須從頭一路讀到尾。阿頁的想法是:連號不是必要的,找得到就好。她把每位旅客的行李切成一頁一頁(每頁固定大小),來一頁就從整座櫃子裡找任何一個空格放進去,然後在自己的對照表上記一筆:「這位旅客的第 3 頁,放在 1 號格」。要讀的時候查表,按頁的順序一格一格去拿。
這樣一來,每個人只占用自己真正需要的格數,格子也不必挨在一起,整座櫃子就像一個共用的池子,誰需要就從池子裡拿。
阿頁的對照表:點任何一頁,看它實際放在哪一格
藍色的是旅客 A 的頁,淡色的是別的旅客的,虛線是空格。新頁會放進任何一個空格。
旅客 A 的對照表(block table)
| 閱讀順序 | 實際位置 |
|---|
還有一個細節要老實說:阿頁的做法也不是零浪費。每頁固定大小,旅客最後一頁常常沒有裝滿,那一點空間還是白占著。但這個浪費最多只有一頁,不會像老管理員那樣一次浪費 4 格、甚至 11 格。
你可能會問:對照表要另外查,讀取會不會變慢?會多一點工,所以 PagedAttention 不只是「換一種分配方式」,它同時包含一套能照對照表讀取分散頁面的注意力運算。這也是它名字裡有 Attention 的原因:光改分配不改讀法,注意力運算會找不到資料。
回到電腦裡
這個想法直接借自作業系統的虛擬記憶體分頁:程式看到的是連續的頁號,實際存放在哪個實體框架由頁表決定。PagedAttention 把 KV Cache 切成固定大小的 block(每個放例如 16 個 token 的 K/V),用 block table 記錄每個請求的邏輯頁到實體頁的對應,注意力核心依表讀取。浪費被限制在每個請求最後一頁未填滿的部分。附帶的好處是共用:同一段系統提示詞的頁,可以讓多個請求指向同一批實體頁,這裡沒有演。
空間管好了,還要決定誰先做
Continuous Batching,以及把這些整合起來的 vLLM
櫃子的問題解決了,但置物櫃區還有另一個瓶頸:服務窗口只有三個。每一輪,三個窗口各替一位旅客處理一步(模型每一輪替每個對話多生成一個字)。旅客的需求長短不同:A 要辦 6 輪,B 只要 2 輪。
老規矩是固定批次:三位一組,這一組全部辦完,下一組才能進來。B 兩輪就辦完了,他的窗口卻空著,因為 A 還在辦。Continuous Batching(動態批次)改成每一輪結束就重新看一次:哪個窗口空了,排隊的人下一輪就補進去。
六位旅客、三個窗口,逐輪看差別
固定批次:整組辦完才換下一組
有人先辦完,窗口也只能空著等。
Continuous Batching:有空位,下一輪就補人
不必等最慢的那位。
把每一輪攤開來看
右邊不是把 A 辦得更快,A 一樣要 6 輪;它只是不讓空出來的窗口閒著。這個模擬假設每輪每位旅客都剛好前進一步,省略了新旅客進場時要先處理整段前文(Prefill)的時間、也不考慮記憶體夠不夠,所以「8 輪對 6 輪」是排程示意,不是效能倍數。
兩件事各管各的:阿頁的對照表管的是櫃子空間,動態批次管的是窗口排程。它們互補,但不是同一項技術;不過在實務上兩者常常一起出現,因為動態批次要能隨時讓新人進場,前提正是櫃子不會被大片預留卡死。
vLLM:把這些整合成一套服務
到這裡,再看 vLLM 就不會只是一串名詞。它是一個推論引擎:接收很多人的請求,用分頁的方式管理 KV Cache,每一輪重新安排批次,再交給 GPU 執行模型運算,並依環境選擇注意力的實作(前一集的 FlashAttention 是其中一種)。
旅客
很多人的請求同時進來,每個人的內容和長度都不同。vLLM(推論引擎)
PagedAttention:分頁管理 KV Cache,依對照表讀取Continuous Batching:每輪重排批次,補進新請求注意力後端:依環境選用,例如 FlashAttentionGPU
執行模型運算,每輪替每個對話多產生一個字。最重要的一句話:這些做法都不會讓模型變聰明。同一份模型權重,換到 vLLM 上跑,回答的品質不會變好;變好的是同樣的硬體能服務更多人、等待時間更短、記憶體浪費更少。另一個常混淆的對照:模型量化壓的是權重的大小,PagedAttention 管的是KV Cache的空間,兩者解決的不是同一件事。
KV Cache
解決:重算把算過的名牌和資料夾留下來。代價是對話越長占越多記憶體。PagedAttention
解決:空間浪費切成固定大小的頁、按需分配、靠對照表讀取分散的頁。浪費最多只剩最後一頁。Continuous Batching
解決:窗口空等每輪重新安排批次,有人辦完就讓排隊的人補進來。vLLM
把上面的整合起來推論引擎,不是模型;提升吞吐與記憶體利用率,不改變回答品質。| 故事裡的 | 真正的名稱 | 一句話說明 |
|---|---|---|
| 旅客 | 請求(request) | 一段正在進行的對話 |
| 行李 | KV Cache | 每個 token 在每層的 K、V,隨對話長度線性增加 |
| 一格 | block(頁) | 固定容量,例如 16 個 token 的 K/V |
| 置物櫃區 | 留給 KV Cache 的 GPU 記憶體 | 多人共用的池子 |
| 先鎖 12 格連號 | 依最大長度預先配置連續記憶體 | 造成內部碎片(沒資料卻占著)與外部碎片 |
| 對照表 | block table | 每個請求的邏輯頁 → 實體頁 |
| 照表讀取分散的頁 | PagedAttention 的注意力核心 | 不改讀法,改分配沒有用 |
| 三個窗口、一輪一步 | 批次名額、一個生成步驟 | 「窗口」是名額的比喻,不是實體 GPU 核心 |
| 整組辦完才換 | Static Batching(固定批次) | 先完成的名額空等 |
| 每輪補人 | Continuous Batching(動態批次) | 以生成步驟為單位重排,源自 Orca 的想法 |
| 置物櫃區的整套管理 | vLLM | 推論引擎,整合以上能力 |
故事沒有說完的部分
這一頁的置物櫃只有 60 格、一格等於一頁、行李剛好填滿每一頁;真實系統一頁裝固定數量的 token,最後一頁通常沒滿,而且還要留空間給模型權重和運算中的暫存。批次模擬也省略了 Prefill 的時間、每輪耗時的差異和記憶體是否夠用。
PagedAttention 還有幾個延伸這裡沒演:多個請求共用同一段前綴(例如相同的系統提示詞)的頁、記憶體不足時把某些請求的頁先換出去再換回來,以及不同排程策略的取捨。這些都建立在「頁可以分散、靠對照表找」這個基礎上。
四個問題,確認真的看懂
選了之後會告訴你原因
延伸閱讀
- Efficient Memory Management for Large Language Model Serving with PagedAttention(原始論文:碎片、分頁、block table、共用與換出)
- vLLM:Introducing PagedAttention(按需分配、非連續頁面、最後一頁的浪費)
- Orca: A Distributed Serving System for Transformer-Based Generative Models(以生成步驟為單位的批次排程)
- vLLM 官方文件(推論引擎、快取管理與批次)
- Hugging Face:How caching works(KV Cache 的用途與成長)
本頁所有格數、入場人數、輪數皆由瀏覽器依教學規則即時計算,不呼叫任何模型或服務,也不傳送資料。