JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

馴服 KV 快取:分頁、前綴與量化

KV 快取是服務記憶體最大的單一消耗者,也是你能同時容納多少請求的關卡。分頁、前綴重用、分塊預填與量化這四個想法,把它從負擔變成一項受管理的資源。

為何快取是並行度的瓶頸

在權重之後,KV 快取 是 GPU 記憶體第二大的占用者;與固定的權重不同,它隨每個 token、每個並行請求而增長。其大小約為層數 × 2 × 頭數 × 每頭維度 × 序列長度 × 批次 × 每元素位元組數。對於長 脈絡視窗 與眾多使用者,這可能遠超過模型本身,也正是你無法靠加大批次來塞滿 GPU 的真正原因:算力還沒用完,快取記憶體就先耗盡了。

M_{\mathrm{KV}} = 2\,L\,n_{\mathrm{kv}}\,d_{\mathrm{head}}\,s\,b\,p

KV 缓存占用随层数 L、KV 头数 n_kv、头维度 d_head、序列长度 s 和批大小 b 线性增长——因子 2 同时计入键和值,p 为每个元素的字节数。

PagedAttention:為快取設計的虛擬記憶體

傳統服務為每個請求配置一塊連續的快取緩衝區,並依最大可能長度配置。這因內部碎裂浪費了大量記憶體:提早結束、或從未達到最大長度的請求,會留下別人無法使用的空洞。PagedAttention 借用了作業系統的虛擬記憶體概念。它把快取切成固定大小的區塊(block,分頁),並讓請求的邏輯序列透過區塊表(block table)映射到不連續的實體區塊。

回報是近乎零的碎裂:區塊按需配置,請求一結束就立即釋放,於是記憶體利用率從大約一半攀升到九成以上。更高的利用率代表能容納更多請求,代表更大的批次,而——回想第一篇那道牆——代表更高的吞吐量。PagedAttention 是幾乎每個現代引擎賴以建立的基礎。

前綴快取:絕不重算共用的前綴

許多請求共享開頭的前綴——一段長的系統提示、幾個示範樣本的範本、一份檢索到的文件、一段多輪對話歷史。為每個請求重算那段 prefill 是純粹的浪費。前綴快取(prefix caching) 保留已見過前綴的 KV 區塊,讓新請求附掛上去,而非重新預填。由於 PagedAttention 本就把快取存成可共享的區塊,兩個前綴相同的請求只要把各自的區塊表指向同一批實體分頁即可。

前缀缓存复用注意力查找算出的每一层键和值,让共享前缀无需重复预填充。

注意力作为软查询—键—值查找的示意图,其键和值正是缓存所复用的内容。

分塊預填:讓延遲保持平穩

長提示帶來一個問題:預填它是一次龐大、運算密集的爆發,會獨占 GPU,使其他所有請求的 decode 停擺,造成 TPOT 尖峰。分塊預填(chunked prefill) 把長 prefill 切成較小的片段,並在同一批次中將這些片段與進行中的 decode 步交錯。沒有任何一次迭代被單一巨大的 prefill 主導,因此即使有超長提示湧入,decode 延遲依然平穩。

分塊預填也給排程器一個連續的旋鈕,用來平衡 prefill 與 decode 的工作量——這個主題我們會在下一篇完整展開。此處的啟示是:prefill 與 decode 爭奪同一份硬體,而良好的服務在很大程度上就是優雅地排程這場競爭。

KV 快取量化:每位元組裝下更多脈絡

若記憶體頻寬與容量是瓶頸,就用更少的位元儲存快取。KV 快取量化 把鍵與值存成 8 位元甚至 4 位元,而非 16 位元,立刻把快取占用與每個 decode 步搬動的位元組減半或減為四分之一。這讓你能在同一張 GPU 上裝下更長的脈絡與更大的批次。

\hat{x} = s\cdot\mathrm{round}\!\left(\dfrac{x}{s}\right),\quad s = \dfrac{\max_i |x_i|}{2^{\,b-1}-1}

对称 b 位量化用一个共享缩放因子 s 把每个键/值存成整数;从 16 位降到 8 位或 4 位,正是用更少字节换取更多上下文的方式。