戰(zhàn)指南:RadixAttention如何讓重復(fù)請求不再白算)
SGLang前綴緩存實(shí)戰(zhàn)指南RadixAttention如何讓重復(fù)請求不再白算【免費(fèi)下載鏈接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/sg/sglang本文面向初次接觸 SGLang 推理框架的開發(fā)者用大白話講清楚前綴緩存RadixAttention為什么能省錢、內(nèi)部怎么運(yùn)作、如何開啟與調(diào)優(yōu)。讀完你就能在自己的服務(wù)里把相同開頭只算一次這件事落地實(shí)測排隊(duì)時(shí)間與顯存占用雙雙下降。先講一個(gè)讓人肉疼的真實(shí)場景想象你維護(hù)著一個(gè)客服問答機(jī)器人每個(gè)用戶進(jìn)來系統(tǒng)都要拼一段固定指令比如你是XX平臺(tái)的客服請用中文、簡潔地回答再加上之前幾輪的歷史對(duì)話一起發(fā)給大模型。假設(shè)這段前綴有 800 個(gè) token100 個(gè)用戶同時(shí)來問問題模型就要把這 800 個(gè) token 從頭到尾算 100 遍。問題就出在大模型的自回歸特性上每生成一個(gè) token它都要重新讀取一遍全部歷史 token 的注意力狀態(tài)。生成前的那段預(yù)計(jì)算業(yè)內(nèi)叫prefill預(yù)填充非常昂貴而如果大家的開頭一模一樣這筆錢就純粹是被重復(fù)燒掉的。下面這張圖來自 SGLang 項(xiàng)目文檔docs/images/dpa.png展示了推理流水線里 prefill 與 decode 兩個(gè)階段的調(diào)度分工——prefill 是計(jì)算密集段也正是前綴緩存發(fā)揮作用的主戰(zhàn)場。SGLang 給出的解法叫RadixAttention把已經(jīng)算過的公共前綴連同它的中間結(jié)果一起緩存下來下一個(gè)請求如果開頭相同直接接著算前面那段直接跳過。先看收益再談原理這筆買賣到底劃算不劃算緩存不是萬能的它的收益取決于你的請求有多少公共開頭。拿三個(gè)常見場景舉例業(yè)務(wù)場景公共前綴占比大致收益客服會(huì)話固定系統(tǒng)指令歷史對(duì)話高幾乎每個(gè)請求都帶排隊(duì)延遲大幅下降顯存更穩(wěn)批量代碼補(bǔ)全相同文件上下文中高同倉庫內(nèi)前綴相似補(bǔ)全吞吐明顯提升獨(dú)立的一次性提問毫無公共前綴幾乎為零收益可忽略核心規(guī)律一句話公共前綴越長、出現(xiàn)越頻繁緩存收益越大。多輪對(duì)話之所以受益明顯正是因?yàn)槊恳惠喌男抡埱蠖紟е叭枯喆蔚耐暾麣v史前綴一次比一次長而復(fù)用一次就省一次 prefill。把原理講透一棵會(huì)記住公共前綴的樹RadixAttention 的底層是一棵基數(shù)樹Radix Tree。你可以把它想象成文件系統(tǒng)里的目錄結(jié)構(gòu)/usr/bin和/usr/lib共享/usr這一段磁盤上只存一份。對(duì)應(yīng)到緩存里每一段 token 序列就是目錄整條請求就是路徑。整個(gè)工作機(jī)制可以拆成四步來看匹配新請求從根節(jié)點(diǎn)出發(fā)按 token 逐個(gè)往下比對(duì)找到能復(fù)用的最長公共前綴直接拿到對(duì)應(yīng)的緩存索引跳過這部分計(jì)算。核心邏輯在 python/sglang/srt/mem_cache/radix_cache.py 的match_prefix里。分裂與插入如果新請求的前半段命中了、后半段是新內(nèi)容樹會(huì)在公共前綴的末端長出一個(gè)新分支把新內(nèi)容掛上去下次再有人用同樣開頭就能命中。淘汰回收顯存不夠時(shí)樹會(huì)按最久沒被用過LRU的策略從葉子節(jié)點(diǎn)開始剪枝剪掉的節(jié)點(diǎn)把 token 空間還給緩存池保證內(nèi)存不炸。引用保護(hù)正在被當(dāng)前請求使用的節(jié)點(diǎn)會(huì)打上引用標(biāo)記lock_ref處于保護(hù)狀態(tài)淘汰階段會(huì)繞開它們避免出現(xiàn)數(shù)據(jù)剛?cè)〕鰜砭捅换厥盏膶擂巍m槑б惶釋?duì)于超長 promptSGLang 還支持分塊前綴緩存把長序列切成若干塊按塊粒度去匹配和復(fù)用避免整條差一個(gè) token 就全部失效的浪費(fèi)。三步開啟前綴緩存馬上見效SGLang 默認(rèn)就開著 RadixAttention你大概率已經(jīng)在享受它了。想確認(rèn)或手動(dòng)控制跟著這三步走第一步確認(rèn)沒被關(guān)閉。啟動(dòng)服務(wù)時(shí)檢查命令行參數(shù)里是否出現(xiàn)了--disable-radix-cache這個(gè)參數(shù)存在且被設(shè)置時(shí)緩存才關(guān)。正常啟動(dòng)python -m sglang.launch_server --model-path 你的模型默認(rèn)開啟無需額外操作。第二步按需調(diào)整頁面大小。緩存按頁管理頁面越小粒度越細(xì)、命中越靈活但管理開銷也越大??梢韵扔媚J(rèn)值跑通再根據(jù)命中率微調(diào)--page-size這類參數(shù)。第三步為長序列開啟分塊。如果你的請求前綴普遍很長找到分塊前綴緩存相關(guān)的環(huán)境變量與閾值設(shè)置把閾值調(diào)到合適位置長 prompt 的復(fù)用率通常會(huì)有驚喜。兩個(gè)可以直接抄走的場景示例場景一客服會(huì)話的歷史前綴復(fù)用# 偽代碼示意把系統(tǒng)指令歷史會(huì)話作為公共前綴 system_instruction 你是XX平臺(tái)客服請用中文簡潔作答。 conversation_history 用戶我想退款\n客服請?zhí)峁┯唵翁?hào)\n用戶訂單號(hào)是 8848 queries [現(xiàn)在退到哪一步了, 大概幾天到賬, 還能改成退貨嗎] for q in queries: # 三個(gè)請求共享同一段前綴prefill 只算一次 response ask(system_instruction conversation_history q)三個(gè)問題共享了同一段歷史第二個(gè)、第三個(gè)請求的前綴直接命中緩存模型只計(jì)算真正不同的那部分響應(yīng)速度肉眼可見地變快。場景二批量代碼補(bǔ)全# 同一倉庫內(nèi)文件頭部上下文高度相似 file_context import torch\nimport torch.nn as nn\nfrom typing import List, Optional\n\n snippets [def forward(self, x):, def loss(self, y, pred):, class Trainer:] for snippet in snippets: # 每個(gè)補(bǔ)全請求都帶著相同的 import 前綴這部分只算一次 completion complete(file_context snippet)批量場景里緩存命中率越高整批任務(wù)的完成時(shí)間越接近只算一遍的理想值。看懂這幾個(gè)監(jiān)控指標(biāo)調(diào)優(yōu)才有依據(jù)光開啟不觀察等于盲調(diào)。SGLang 暴露了與緩存直接相關(guān)的指標(biāo)重點(diǎn)關(guān)注這幾個(gè)前綴緩存命中率命中的請求占比是衡量收益的第一指標(biāo)命中率上不去先懷疑場景是否真有公共前綴。可淘汰緩存大小當(dāng)前能被 LRU 回收的空間能幫你判斷緩存還富余多少。受保護(hù)緩存大小被引用鎖占住的空間如果長期很大說明并發(fā)密集注意觀察是否擠壓了可回收空間??偩彺娲笮∨浜巷@存使用率一起看確認(rèn)緩存沒把顯存撐爆。新手最容易踩的3個(gè)坑坑一以為緩存命中率必須 100%。命中率低不一定是配置問題可能只是業(yè)務(wù)本身沒有公共前綴。先檢查請求分布再懷疑參數(shù)。解決思路用上面的指標(biāo)確認(rèn)公共前綴占比別盲目改配置。坑二前綴共享了但差一個(gè) token導(dǎo)致全部失效。比如某個(gè)請求帶了時(shí)間戳或隨機(jī) ID前綴從此千奇百怪。解決思路把動(dòng)態(tài)內(nèi)容從提示詞里剝離出去或交給分塊前綴緩存按塊復(fù)用??尤彺媾c并發(fā)互相打架偶發(fā)報(bào)錯(cuò)。淘汰和引用并發(fā)進(jìn)行時(shí)如果沒有鎖保護(hù)就可能出問題。解決思路SGLang 已用引用計(jì)數(shù)機(jī)制處理這類沖突遇到異常先檢查是否關(guān)閉了緩存保護(hù)相關(guān)的默認(rèn)行為、再檢查顯存是否過小導(dǎo)致淘汰過于激進(jìn)。小結(jié)前綴緩存的本質(zhì)就一句話把重復(fù)的勞動(dòng)變成一次勞動(dòng)。RadixAttention 用一棵基數(shù)樹把公共前綴的中間結(jié)果組織起來配合 LRU 淘汰和引用保護(hù)在顯存安全的前提下把 prefill 的浪費(fèi)壓到最低。上手路徑也很簡單默認(rèn)開啟不用管 → 看命中率 → 有長前綴就開分塊 → 遇到差一個(gè) token就把動(dòng)態(tài)內(nèi)容挪出提示詞。跑一輪觀察下來你會(huì)對(duì)省下來的錢有非常直觀的感受。想深入源碼直接翻 python/sglang/srt/mem_cache/radix_cache.py 和 python/sglang/srt/mem_cache/base_prefix_cache.py比任何教程都實(shí)在?!久赓M(fèi)下載鏈接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/sg/sglang創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考