news 2026/9/2 23:00:48

SGLang 前缀缓存实战:RadixAttention 如何让重复前缀省下 3–5 倍计算

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SGLang 前缀缓存实战:RadixAttention 如何让重复前缀省下 3–5 倍计算

SGLang 前缀缓存实战:RadixAttention 如何让重复前缀省下 3–5 倍计算

【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang

本文围绕 SGLang 的 RadixAttention 前缀缓存展开:它用一棵基数树(Radix Tree)记录「前缀 → 已算好的 KV 缓存位置」的映射,新请求进来先查树、命中多少就免算多少。全文从真实部署场景出发,讲清工作原理、最小可运行示例、加速来源、场景化用法、调参手册和常见坑。

一个真实场景:客服机器人的「每次都在重算」

先讲个具体画面。某团队用 SGLang 部署了一个客服机器人,每条请求都带着同一段约 2000 token 的知识库摘要和角色设定开头,后面才是用户的具体问题。高峰期一小时几千条请求,GPU 的算力几乎全花在这段「每次都一样的开头」上——同一个 2000 token 的 prefill 被反复算了几千遍。

SGLang 为此专门做了 RadixAttention:把已经算过的 KV 缓存按前缀持久化在一张基数树里(基数树可以理解成「压缩版前缀字典树」,相同前缀只存一份),并且默认开启。第二个请求带着相同开头进来时,调度器直接从树上找到已算好的 KV 张量索引,跳过这部分 prefill,只计算新增的 token。效果不是「快一点」,而是同一批请求里,后到请求的首 token 延迟(TTFT)可以砍掉一大截,GPU 的 prefill 吞吐同步放大,端到端常见 3–5 倍的加速区间,具体取决于前缀重合度。

前缀缓存到底解决什么问题:一段前缀,一份计算

用两个比喻把痛点说透。

快递驿站分拣。没有前缀缓存时,每个包裹(请求)都要从产地(模型第一层)开始走完全程。驿站早该建立分拣规则:同一条路(前缀)的包裹,到达同一站之后的路可以共用。RadixAttention 就是那张「按前缀分区」的分拣表——token 序列是地址,共同前缀越长,越晚到的包裹越不用重走老路。

图书馆按书架编号。每个「已经读过的前缀」占一个书架位(KV 缓存的物理位置),编号写进目录树。新读者报出书名开头,管理员顺着目录找到最远的那个共同前缀,直接从下一本接着读。

痛点拆开看是两条因果链:

  • 成本链:前缀重复 → prefill 重复计算 → GPU 计费时间被浪费。因为 prefill 阶段是算力密集型的,一段 2000 token 的公共前缀每多算一遍,就多烧一遍等量的矩阵乘法。
  • 延迟链:前缀重复 → TTFT 必须等完整 prefill 算完 → 用户等待时间里有一大段是「白等」。命中缓存后,这段等待直接消失,只剩新增 token 的计算时间。

所以前缀缓存的收益不是玄学,它能被直接量化:每命中 1 个 token,就省掉 1 个 token 的 prefill 计算。命中率是唯一的杠杆。

一张图看懂 RadixAttention 的工作方式

整个机制可以压缩成「进来查树 → 命中则复用 → 算完插树 → 空间不够再淘汰」四步:

对应到源码,两个核心文件就够了:

  • 树本体:[radix_cache.py](https://link.gitcode.com/i/f21b0ef418d88df98eb7f361ff5fd976)match_prefix找最长已缓存前缀,insert把新算的 KV 挂回树上,evict负责腾空间。每个TreeNode上维护着lock_ref(引用计数,请求在用就保护该节点不被淘汰)和last_access_time(LRU 排序依据)。
  • 开关与参数:[server_args.py](https://link.gitcode.com/i/52d81e3484c60450db7c0a6f5664f701)--disable-radix-cache可整体关闭,--page-size--radix-eviction-policy--enable-hierarchical-cache都在这张参数表里。

一句话总结数据流:请求的 token 序列是钥匙,树里存的是「钥匙 → KV 张量物理位置」的映射;命中即省算,算完再存回去。

5 分钟跑通:从启动服务到验证命中

你马上能拿到的东西:一条启动命令 + 三次请求,亲眼看到缓存命中。

第一步,启动 SGLang 服务(前缀缓存默认开启,无需额外参数):

# 前缀缓存默认启用;--page-size 控制每页 token 数,默认 1 python -m sglang.launch_server \ --model-path Qwen/Qwen2.5-7B-Instruct \ --port 30000

第二步,发三次「同前缀、不同结尾」的请求:

PROMPT='以下是公司FAQ摘要:……(2000 token 的公共前缀)…… 问题:' # 第一条:缓存为空,全量 prefill,最慢 curl -s localhost:30000/generate -d "{\"text\": \"${PROMPT}退货政策是什么?\"}" # 第二、三条:前缀命中,只算最后几个 token,TTFT 明显下降 curl -s localhost:30000/generate -d "{\"text\": \"${PROMPT}运费怎么算?\"}" curl -s localhost:30000/generate -d "{\"text\": \"${PROMPT}几天能到?\"}"

验证是否命中,有两个抓手:

# 1) 服务信息接口:cache_hit_rate 会随重复前缀请求上升 curl -s localhost:30000/get_server_info # 2) Prometheus 指标中也暴露 gpu_prefix_cache_hit_rate curl -s localhost:30000/metrics | grep prefix_cache_hit

看到cache_hit_rate从第一条请求的低值逐步爬升,就说明树在正常工作。想彻底关掉做对照实验,加--disable-radix-cache即可。

它凭什么快 3–5 倍:把命中率翻译成省下的计算

先给结论:加速不来自单次请求变快,而来自「重复前缀」从计算图里消失了。

因果链是这样的:

  1. 一批请求共享同一段前缀,长度 P token;
  2. 只有第一个请求付出 P 次 prefill 的计算,后续请求的这段直接查表拿到 KV 索引;
  3. 省下的 prefill 时间线性叠加到 GPU 批处理吞吐上,TTFT 同步下降;
  4. 如果前缀长到超出 KV 池容量后被 LRU 淘汰,加速就打折——命中率是上限,淘汰策略决定你能吃到多少

用「省下的 token 数」而不是百分比来表达收益,更直观。假设公共前缀 P = 4000 token,一批 100 条请求:

缓存策略这批请求实际付出的 prefill 计算量相比全量重算省掉的部分
每次请求独立重算(无缓存)100 × 4000 = 40 万 token0
RadixAttention 前缀全命中1 × 4000 + 各请求独立部分约 39.6 万 token 的 prefill
命中率 50%(部分被淘汰)约 20 万 token约 20 万 token

换算成体感:prefill 占大头的工作负载里,前缀部分省掉九成计算,对应的就是 TTFT 从「等整段算完」变成「等最后几个 token 算完」,端到端 3–5 倍的加速区间就是这么来的。前缀越短、请求越离散、KV 池越紧张,收益越接近 1 倍——它不是魔法,是「别算第二遍」的算术

这张饼图传达的重点是:缓存把最贵的部分从「算力」变成了「显存读取」,算力被腾出来给真正的新 token。

把前缀缓存用起来:四类高命中场景

前缀缓存的收益 = 前缀重合度 × 前缀长度 × 请求频次。下面四类业务天然凑齐这三个条件。

客服知识库问答。每条请求都带「角色设定 + 知识库文档 + 工具说明」三段固定内容,只有最后的用户问题变化。把这三段稳定放在 prompt 最前面,重合度接近 100%,是最典型的受益场景。

Agent 工具调用循环。Agent 每轮都会把「系统指令 + 工具 schema + 历史动作记录」重新拼进 prompt。历史只增不改,每一轮都在复用上一轮的前缀——轮数越多,省得越多。关键约束:历史部分不要每轮重排或改写,前缀一变,树就断在第一个分叉处。

RAG 长文档问答。对同一份 5 万 token 的文档提 20 个问题,文档 chunk 放在前缀位置,20 条请求只付 1 次文档的 prefill 成本。文档切换频率越高,命中率越低,这时就该考虑下一节的 HiCache。

评测批量跑分。benchmark 批量请求往往共享同一题面模板或 system prompt。把同模板请求排在一起提交,比打散提交命中率高一档——连续提交对树很友好,打散则反复制造分叉。

四类场景的共同操作原则一句话:固定内容前置、可变内容后置、别动已经发过的部分。

调优与进阶:一张调参手册

把分散的机制整理成这张表,参数名全部对应实际的--启动参数:

调什么参数 / 机制默认什么时候动它预期效果
命中率cache_hit_rate/get_server_info/metricsgpu_prefix_cache_hit_rate任何调优前先读它长期低于 30% 时,先排查 prompt 结构,再谈调参
整体开关--disable-radix-cache不关闭做 A/B 对照实验关闭后 prefill 全量重算,可做收益归因
页粒度--page-size1配合分页注意力 / 稀疏 KV 特性时按后端要求调整页越大,对齐浪费越多,管理开销越小
淘汰策略--radix-eviction-policylru/lfu/slru/prioritylru访问模式不均衡、少数热前缀反复出现 →lfuslru更稳热前缀留存时间变长,命中率上限提高
长前缀跨请求留存--enable-hierarchical-cache+--hicache-ratio(cache 模式默认 2.0,即主机 KV 池 = 2 倍设备池)关闭前缀总长超过 GPU 容量、主机内存充足GPU 淘汰的前缀下沉到主机内存,重命中时搬回,省一次完整 prefill
HiCache 写策略--hicache-write-policywrite_back/write_through/write_through_selective主机带宽紧张时选 selective,只写热数据减少无谓的主机写入
分块前缀缓存--disable-chunked-prefix-cache(默认开启,面向 DeepSeek 系模型的分块前缀处理)开启短序列为主的负载,分块簿记开销不划算时关闭短请求省掉分块开销
命名空间隔离RadixKeyextra_key(区分 LoRA / 多版本 RAG 上下文)多 adapter、多版本知识库混跑不同extra_key的条目互不串用,避免脏命中

三个经验值供参考:

  • 命中率长期 < 30%:别急着调参,先检查 prompt 拼装顺序,是不是把可变内容放在了前面;
  • 命中率 > 60% 且 TTFT 已经很低:显存可以挪给更大的 batch,并发上去了总成本反而降;
  • 开了 HiCache 之后命中率仍掉:说明前缀总量超过「GPU + 主机」两层之和,该考虑第三层存储后端(如 mooncake、hf3fs、aibrix 这类,见mem_cache/storage/目录)。

常见误区与踩坑实录

问:命中率很高,为什么 TTFT 没降多少?查请求顺序。前缀缓存是「按到达顺序」吃的——第一批全量算,后面才受益。如果你的压测是单条串行发,前几条请求延迟难看的;换成并发压测,整体 TTFT 和吞吐才体现真实收益。

问:system prompt 里加了一行当前时间戳,命中率直接腰斩?对,而且只保留到第一个不同 token 为止。前缀匹配严格从左到右,第一个不同的 token 之后全部重算。时间戳、request_id、每次变化的占位符这类字段,要么挪到 prompt 末尾,要么改成完全一致的静态文案。

问:显存没满,缓存为什么还会被淘汰?KV 池是独立分配的:模型权重、激活、batch 预留之外才轮到 KV 池。而且请求正在解码时,它占用的节点lock_ref > 0,不参与淘汰;请求结束才释放。所以「显存占用 70%」和「KV 池水位」是两回事,看/get_server_info的 token 用量比看 nvidia-smi 更准。

问:多个 TP rank,前缀树不会各算各的吧?不会错乱。KV 缓存按 TP 切分后各 rank 维护自己那份分片,匹配长度由调度器统一决策。监控里的gpu_prefix_cache_hit_rate是调度器视角的统一口径,不用担心多卡数据不一致。

问:前缀缓存会「污染」输出吗?A 请求的 KV 被 B 用错了?匹配键是完整 token 序列(可加extra_key命名空间),B 的前缀必须逐 token 等于树里已存前缀才会复用;不同 LoRA、不同 adapter 走不同的extra_key,物理上不会串用。

问:多轮对话历史越滚越长,缓存还划算吗?划算。每轮「历史 + 新问题」里,历史部分是严格的前缀扩展,天然高命中;真正开始吃缓存的是第 2 轮起的每一轮。第 1 轮没有前缀可复用,别把它算进收益预期。

问:开了 HiCache,延迟是变好还是变坏?分层看:命中 GPU 层的请求不受影响;命中主机层的请求多了「内存搬回 GPU」的传输,比纯 prefill 快、比 GPU 直取慢——总体仍是赚的,因为省掉的是完整的逐层计算。只有主机带宽被写策略打满时才会劣化,这时换write_through_selective

写在最后

把整件事收回业务口径:前缀缓存把「重复前缀」从计算账单里划掉,换回来的是三样东西——更低的单次 TTFT、更高的单卡并发、以及按 token 计费的推理成本直接缩水。对短 prompt 的单条请求,它是锦上添花;对带长 system prompt、长文档、长 Agent 历史的推理服务,它是「同一张卡,服务两到三倍流量」的关键开关。

前缀缓存不改变模型,它改变的是模型该被计算几次——而「少算」,永远是推理系统里最便宜的性能。

【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 23:00:41

Convert to it PDF处理双路径解析:pdf-parse与pdftoimg各擅胜场

Convert to it PDF处理双路径解析&#xff1a;pdf-parse与pdftoimg各擅胜场 【免费下载链接】convert Truly universal online file converter 项目地址: https://gitcode.com/GitHub_Trending/convert7/convert Convert to it 是一款完全运行在浏览器中的通用在线文件转…

作者头像 李华
网站建设 2026/9/2 22:56:43

GitHub MCP Server 接入实战:远程与本地 Docker 双路径配置详解

GitHub MCP Server 接入实战&#xff1a;远程与本地 Docker 双路径配置详解 【免费下载链接】github-mcp-server GitHubs official MCP Server 项目地址: https://gitcode.com/GitHub_Trending/gi/github-mcp-server 想让 AI 替你在仓库里查 Issue、改 PR&#xff0c;它…

作者头像 李华
网站建设 2026/9/2 22:56:11

三款一键生成论文工具横评:从构思到提交怎么选才不踩坑?

写论文这事&#xff0c;最怕的不是写不出来&#xff0c;而是写得心里没底。 题目改了七八版还怕选重了&#xff0c;文献下载了两百篇越读越乱&#xff0c;参考文献格式调到崩溃&#xff0c;交稿前还得担心重复率和AIGC检测。今年开学季一到&#xff0c;又有一波人在搜“AI论文工…

作者头像 李华