拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SGLang前缀缓存实战指南:RadixAttention如何让重复请求不再白算

SGLang前缀缓存实战指南RadixAttention如何让重复请求不再白算【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang本文面向初次接触 SGLang 推理框架的开发者用大白话讲清楚前缀缓存RadixAttention为什么能省钱、内部怎么运作、如何开启与调优。读完你就能在自己的服务里把相同开头只算一次这件事落地实测排队时间与显存占用双双下降。先讲一个让人肉疼的真实场景想象你维护着一个客服问答机器人每个用户进来系统都要拼一段固定指令比如你是XX平台的客服请用中文、简洁地回答再加上之前几轮的历史对话一起发给大模型。假设这段前缀有 800 个 token100 个用户同时来问问题模型就要把这 800 个 token 从头到尾算 100 遍。问题就出在大模型的自回归特性上每生成一个 token它都要重新读取一遍全部历史 token 的注意力状态。生成前的那段预计算业内叫prefill预填充非常昂贵而如果大家的开头一模一样这笔钱就纯粹是被重复烧掉的。下面这张图来自 SGLang 项目文档docs/images/dpa.png展示了推理流水线里 prefill 与 decode 两个阶段的调度分工——prefill 是计算密集段也正是前缀缓存发挥作用的主战场。SGLang 给出的解法叫RadixAttention把已经算过的公共前缀连同它的中间结果一起缓存下来下一个请求如果开头相同直接接着算前面那段直接跳过。先看收益再谈原理这笔买卖到底划算不划算缓存不是万能的它的收益取决于你的请求有多少公共开头。拿三个常见场景举例业务场景公共前缀占比大致收益客服会话固定系统指令历史对话高几乎每个请求都带排队延迟大幅下降显存更稳批量代码补全相同文件上下文中高同仓库内前缀相似补全吞吐明显提升独立的一次性提问毫无公共前缀几乎为零收益可忽略核心规律一句话公共前缀越长、出现越频繁缓存收益越大。多轮对话之所以受益明显正是因为每一轮的新请求都带着之前全部轮次的完整历史前缀一次比一次长而复用一次就省一次 prefill。把原理讲透一棵会记住公共前缀的树RadixAttention 的底层是一棵基数树Radix Tree。你可以把它想象成文件系统里的目录结构/usr/bin和/usr/lib共享/usr这一段磁盘上只存一份。对应到缓存里每一段 token 序列就是目录整条请求就是路径。整个工作机制可以拆成四步来看匹配新请求从根节点出发按 token 逐个往下比对找到能复用的最长公共前缀直接拿到对应的缓存索引跳过这部分计算。核心逻辑在 python/sglang/srt/mem_cache/radix_cache.py 的match_prefix里。分裂与插入如果新请求的前半段命中了、后半段是新内容树会在公共前缀的末端长出一个新分支把新内容挂上去下次再有人用同样开头就能命中。淘汰回收显存不够时树会按最久没被用过LRU的策略从叶子节点开始剪枝剪掉的节点把 token 空间还给缓存池保证内存不炸。引用保护正在被当前请求使用的节点会打上引用标记lock_ref处于保护状态淘汰阶段会绕开它们避免出现数据刚取出来就被回收的尴尬。顺带一提对于超长 promptSGLang 还支持分块前缀缓存把长序列切成若干块按块粒度去匹配和复用避免整条差一个 token 就全部失效的浪费。三步开启前缀缓存马上见效SGLang 默认就开着 RadixAttention你大概率已经在享受它了。想确认或手动控制跟着这三步走第一步确认没被关闭。启动服务时检查命令行参数里是否出现了--disable-radix-cache这个参数存在且被设置时缓存才关。正常启动python -m sglang.launch_server --model-path 你的模型默认开启无需额外操作。第二步按需调整页面大小。缓存按页管理页面越小粒度越细、命中越灵活但管理开销也越大。可以先用默认值跑通再根据命中率微调--page-size这类参数。第三步为长序列开启分块。如果你的请求前缀普遍很长找到分块前缀缓存相关的环境变量与阈值设置把阈值调到合适位置长 prompt 的复用率通常会有惊喜。两个可以直接抄走的场景示例场景一客服会话的历史前缀复用# 伪代码示意把系统指令历史会话作为公共前缀 system_instruction 你是XX平台客服请用中文简洁作答。 conversation_history 用户我想退款\n客服请提供订单号\n用户订单号是 8848 queries [现在退到哪一步了, 大概几天到账, 还能改成退货吗] for q in queries: # 三个请求共享同一段前缀prefill 只算一次 response ask(system_instruction conversation_history q)三个问题共享了同一段历史第二个、第三个请求的前缀直接命中缓存模型只计算真正不同的那部分响应速度肉眼可见地变快。场景二批量代码补全# 同一仓库内文件头部上下文高度相似 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: # 每个补全请求都带着相同的 import 前缀这部分只算一次 completion complete(file_context snippet)批量场景里缓存命中率越高整批任务的完成时间越接近只算一遍的理想值。看懂这几个监控指标调优才有依据光开启不观察等于盲调。SGLang 暴露了与缓存直接相关的指标重点关注这几个前缀缓存命中率命中的请求占比是衡量收益的第一指标命中率上不去先怀疑场景是否真有公共前缀。可淘汰缓存大小当前能被 LRU 回收的空间能帮你判断缓存还富余多少。受保护缓存大小被引用锁占住的空间如果长期很大说明并发密集注意观察是否挤压了可回收空间。总缓存大小配合显存使用率一起看确认缓存没把显存撑爆。新手最容易踩的3个坑坑一以为缓存命中率必须 100%。命中率低不一定是配置问题可能只是业务本身没有公共前缀。先检查请求分布再怀疑参数。解决思路用上面的指标确认公共前缀占比别盲目改配置。坑二前缀共享了但差一个 token导致全部失效。比如某个请求带了时间戳或随机 ID前缀从此千奇百怪。解决思路把动态内容从提示词里剥离出去或交给分块前缀缓存按块复用。坑三缓存与并发互相打架偶发报错。淘汰和引用并发进行时如果没有锁保护就可能出问题。解决思路SGLang 已用引用计数机制处理这类冲突遇到异常先检查是否关闭了缓存保护相关的默认行为、再检查显存是否过小导致淘汰过于激进。小结前缀缓存的本质就一句话把重复的劳动变成一次劳动。RadixAttention 用一棵基数树把公共前缀的中间结果组织起来配合 LRU 淘汰和引用保护在显存安全的前提下把 prefill 的浪费压到最低。上手路径也很简单默认开启不用管 → 看命中率 → 有长前缀就开分块 → 遇到差一个 token就把动态内容挪出提示词。跑一轮观察下来你会对省下来的钱有非常直观的感受。想深入源码直接翻 python/sglang/srt/mem_cache/radix_cache.py 和 python/sglang/srt/mem_cache/base_prefix_cache.py比任何教程都实在。【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门