
如果你最近在折腾大模型本地部署大概率已经见过 SGLang 这个名字。我自己是从 vLLM 转过来的最早打动我的不是它标榜的吞吐量数字而是它对“缓存”这件事的认真程度。这篇想聊的 HiCache就是 SGLang 缓存体系里值得单独拿出来讲清楚的一块。它解决的问题非常朴素同样的前缀输入为什么要一遍又一遍地重新计算尤其当你拿 SGLang 离线部署 Qwen3-8B、跑私有知识库问答这类场景时有没有把这个机制吃透直接决定你在无 NVLink 的普通 PC 上能不能流畅用起来以及对比 vLLM、Ollama、LM Studio 时到底该选谁。这篇文章不打算全面介绍 SGLang 的所有功能只围绕 HiCache 展开并把“无 NVLink 影响多大”这个问题一并算清楚。1. 先搞清楚 HiCache 在解决什么问题大模型生成每个 token 时都要读取历史 token 的 Key 和 Value这套 KV Cache 是 Transformer 推理加速的基石。没有它模型每出来一个字前面所有字都要重新算一遍别说多轮对话单是长文本阅读都跑不动。缓存这件事单看不算新概念问题在于它在实际部署中很容易被用得很粗糙。1.1 长上下文里的“重复劳动”有多夸张假设你有一个 8K token 的 system prompt——比如一段企业知识库的规则说明或者一份长时间跨度的数据报告。用户每问一个问题模型都要先把这 8K 前缀完整过一遍生成中间状态的 KV Cache。如果服务端没有任何跨请求的缓存复用那 10 个用户同时问 10 个不同问题这 8K 前缀就被白算 10 遍。我实测过一个不算极端的场景单张 RTX 4090 上跑 8B 模型一次 8K 长度的 prefill 大约要 3 到 5 秒。如果并发 50 个携带同一长前缀的请求光重复计算就能把整张卡的吞吐直接吃没。更烦的是多轮对话里前面几轮的内容在每一轮都会被重新当作前缀处理对话越长浪费越大。H iCache 这种机制要解决的核心痛点就是这个——只要是出现过的前缀就不许再重算。1.2 HiCache 和传统 KV Cache 的定位差异传统 KV Cache 是请求内部的缓存生命周期跟单个请求绑定请求结束就释放。它解决的是“一个请求内部不重复计算”的问题。HiCache 则跨出了一大步它把缓存放进了跨请求、跨层级的结构里。请求 A 算过某个前缀之后请求 B 如果带同样的前缀可以直接从缓存里把对应的 KV block 接上整个 prefill 阶段直接跳过去只计算两者不同的那部分后缀。用生活类比可能更好理解。普通 KV Cache 像是你在图书馆随手翻开一本书看完放回去下次再找还得重新翻目录HiCache 则是你手里捏着一张个人化书签还顺便把上次翻到的那几页复印件揣在兜里。读同一本书的人越多、读的次数越频繁书签的价值就越大。1.3 什么人应该认真读这篇文章如果你是下面三类人之一HiCache 相关信息值得读完并自己验证一遍在跑本地大模型服务希望提高并发、降低首 token 延迟而不是无脑堆显卡正在比较 SGLang、vLLM、Ollama、LM Studio 这些框架想搞清楚“引擎不同到底差在哪”手上是几张没有 NVLink 的消费级显卡尤其多张 4090想知道多卡分布式推理到底能不能碰。我后面所有论述都会落到这三类需求上不空谈理论。2. 分层缓存机制到底改了哪几层东西HiCache 这个名字我理解成 Hierarchical Cache也就是分层缓存。它在 SGLang 原有的 RadixAttention 基础上继续演进把缓存放进了一个多级结构而不是把所有希望都押在昂贵的 GPU 显存上。2.1 先用 RadixAttention 打个底SGLang 最早吸引我的一点是它的前缀缓存算法不搞简单哈希而是用基数树Radix Tree来组织共享前缀。基数树的好处是能高效处理“部分共享”的情况两个请求的前 100 个 token 相同但后面不同它们在树里共用一个父节点路径各自长出分支后续计算时相同的 100 token 对应的 KV Cache 只要有一份就够了。SGLang 的这套机制叫 RadixAttention它在调度层做了很多精妙设计比如 LRU 淘汰策略、缓存块的重组等等。以前我误以为前缀缓存就是把整段 prompt 做一次精确匹配实践下来发现根本不够用——真实场景里请求永远是既有共享又有差异基数树这种结构天生就是干这个的。2.2 HiCache 的三级缓存显存、内存、磁盘HiCache 这层演进粗看就是借鉴了计算机体系结构里经典的 L1/L2/主存/硬盘分层思路。它把 KV Cache 的存储层级拆分成了三层GPU HBM 作为 L1存放最热点的前缀访问速度最快但容量最小CPU DRAM 作为 L2放一批还算常用的缓存块显存放不下就溢到这里磁盘NVMe/SATA SSD作为 L3容纳最冷门的大量历史缓存块反正便宜、容量大。关键点在于它不是一个笨拙的“冷了就淘汰”逻辑而是“冷了就先降级存放”。当某个前缀在 L1 显存里没命中但能在 L2 或 L3 中找到时引擎会把对应的缓存块异步换回显存然后再做增量计算而不是整段重新 prefill。这个设计思路说白了就是宁可多花一次换入换出的 I/O也绝不返工重算一整段几百上千的 token。2.3 命中率怎么影响真实吞吐我拿自己的机器做过一组对比试验环境是单张 RTX 4090 24G、Qwen3-8B、静态显存占比设为 0.85、最大上下文 32K、并发 32。所有结果只代表我个人环境但趋势很有参考意义。第一组场景是纯粹的短请求每个 prompt 之间没有任何共享前缀。此时缓存几乎帮不上忙实测吞吐大约在 1300 tokens/s 上下这基本就是原始算力上限。第二组场景是所有请求共享一段 4K 的 system prompt。开了默认的 RadixAttention 之后TTFT首 token 延迟肉眼可见地降低整体吞吐爬到了 1600 tokens/s 左右。第三组场景最接近生产环境所有请求共享一段 12K 的文档前缀同时并发更高。只靠 GPU 显存缓存时显存很快被文档的 KV Cache 占满并发一高就开始抖动。启用了分层缓存之后一部分冷数据下沉到内存和磁盘GPU 显存被释放出来实际吞吐从 1400 出头拉到了接近 2100而且 32K 长上下文的请求不再容易中途 OOM。命中率每提高 10 个百分点收益都不是线性的。因为缓存命中的不仅是一段计算量还连带减少了显存占用给更大的并发 batch 腾出了空间。推理吞吐量和显存使用率往往是联动的HiCache 的高明之处就在于它同时优化了这两个变量。2.4 值得关注的启动参数SGLang 的缓存机制在不同版本里的实现细节和参数名一直在变我下面列的是我当时用的版本具体以你手上的版本文档为准。但有几个方向是通用的默认 Radix Cache 是开启的不需要额外设置如果你发现日志里有一堆重新 prefill先怀疑是不是有人显式加了禁用参数给 CPU 内存或磁盘分配缓存空间时需要关注启动命令里的内存分配参数不要图省事全部丢给 GPU尤其在长上下文场景日志和 metrics 接口里可以观察到缓存命中率这是调优的第一手依据。参数的精确值不是重点重点是你要清楚SGLang 不是只能把缓存放显存它能往内存和磁盘溢写而 HiCache 往前走的这一步把溢写变成了一种更聪明的分层策略。3. 没有 NVLink 的机器上折腾 SGLang影响到底多大这个问题在相关讨论里被反复提起非常真实。因为很多人在本地玩模型用的是两张 RTX 4090而 4090 这代消费卡直接被砍掉了 NVLink 支持两张卡之间只有 PCIe 通道可走。在这个问题上我踩过很多次坑直接把结论和账目都摆出来。3.1 先算一笔通信账NVLink 和 PCIe 的带宽差距不是一倍两倍是数量级的差距。A100 的 NVLink 单向带宽大约 600GB/sH100 能到 900GB/s而 PCIe 4.0 x16 两卡之间实测有效带宽也就 20 到 30GB/sPCIe 5.0 x16 大概能到 50 到 60GB/s。也就是说走 PCIe 通信比走 NVLink 慢了至少 20 到 30 倍。SGLang 采用张量并行TP时模型权重被切到多张卡上每一层 Transformer 的前向计算里Attention 和 MLP 都要做跨卡同步通常是 all-reduce 或 all-gather。我做了一个很粗的估算假设 8B 模型 hidden size 4096、层数 32BF16 精度单请求 1024 tokensTP2 时每层做过一次跨卡同步大约要传 1024×4096×2 字节等于 8MB 的数据。一个请求前向过 32 层每次至少两三次同步累计下来跨卡通信量在 0.5GB 以上。PCIe 带宽按 25GB/s 算光通信就要 20 到 50 毫秒而在 NVLink 上这个量级在 1 到 2 毫秒内就能完成。3.2 哪些环节会被无 NVLink 放大不是所有操作都会被通信瓶颈拖垮但下面几个场景确实非常难受长输入的 prefill 阶段。通信量随 sequence length 线性增长上下文越长PCIe 短板越刺眼小 batch 请求。通信延迟没法被大量计算掩盖就像高速收费站车少的时候每辆车过闸的固定时间才是关键decode 阶段虽然每次只生成一个 token但每生成一个 token 都要跨卡同步一次虽然单次数据量不大累积起来也拖慢生成速度缓存恢复。TP 模式下某一张卡要读取的缓存块可能恰好分布在另一张卡的显存里无 NVLink 时这个跨卡读取同样走 PCIe。3.3 我实测下来的感受我自己有两套配置单张 4090以及两张 4090 走 PCIe 互联。在两个环境跑同一个 Qwen3-8B结论非常明确单卡能放下模型和 KV Cache 的绝对不要用双卡 TP。单卡下无 NVLink 的影响为零因为根本没有跨卡通信这回事。模型确实大到单卡放不下时我的第一选择不是 TP而是数据并行DP。两张卡各跑一个完整的服务实例外面用负载均衡把请求分开发到两张卡上两张卡之间完全不通信。吞吐量几乎线性增长唯一丢失的是单请求级别的显存容量上限但这在大多数 8B、14B 甚至 32B 量化模型场景下完全可接受。必须上 TP 的场景我会尽量把 batch 做大。batch 越大单次通信里摊掉的计算越多PCIe 短板的影响越小。小并发、长上下文的组合对无 NVLink 的 TP 最不友好尽量避免。3.4 给无 NVLink 用户的操作建议总结成四条可以直接抄模型单卡能放就单卡跑别创造机会让通信成为瓶颈非要多卡扩展吞吐优先数据并行而不是张量并行只有模型超单卡显存才考虑 TP此时把 batch 和并发做上去别拿短请求打长文本长上下文场景善用分层缓存把一部分 KV Cache 溢写到内存和磁盘比硬件互联升级便宜得多。我后来把主力方案定为“单卡模型 分层缓存 数据并行扩展”这套组合在无 NVLink 环境里非常稳。4. 不吹不黑SGLang、vLLM、Ollama、LM Studio 怎么选这个话题的讨论度一直很高尤其很多刚入门的用户在四个选项面前容易蒙圈。我的观点是它们不是同类东西硬比梯度意义不大。关键是理解各自定位然后按场景选。4.1 四个工具各自的性格Ollama 像傻瓜相机装好就能拍内置量化模型库一条命令拉模型一条命令启动特别适合第一次接触本地大模型的人。它的缺点是灵活性差高级推理参数能动的少缓存机制也比较基础生产环境几乎没人拿它当服务后端。LM Studio 是带了一个漂亮屏幕的傻瓜相机图形界面做得非常讨喜适合桌面用户一边试模型一边看参数下载管理、模型切换都顺手。它本质上也不是高性能服务引擎不适合高并发调用。vLLM 是专业单反生态成熟OpenAI 兼容接口做得好量化支持广在工业界有大量验证。它的前缀缓存主要走 PageAttention 路线虽然也能复用共享前缀但在非常规前缀结构和长上下文场景下调度灵活性和缓存命中率和 SGLang 相比还是有差距。SGLang 是带了电脑控制的专业机学习曲线更陡配置更细但换来的是更激进的调度策略、RadixAttention 这类缓存结构以及在 HiCache 分层缓存上的演进。它现在也支持多模态结构化输出约束做得尤其好。4.2 一张表看懂差异维度SGLangvLLMOllamaLM Studio定位高性能推理服务高性能推理服务本地快速使用桌面图形化使用缓存机制RadixAttention 分层缓存演进PageAttention前缀复用有限基础 KV Cache基础 KV CacheOpenAI 兼容 API支持支持支持支持结构化输出约束强一般弱弱多模态支持原生支持部分支持部分支持部分支持部署复杂度较高中低低典型场景生产服务、共享长前缀、高并发生产服务、生态成熟个人初体验桌面试模型4.3 我给不同人群的结论第一次玩本地模型先用 Ollama 或 LM Studio别一上来就折腾 SGLang没必要。等你有明确性能需求比如同时服务几十个人、跑长文档知识库、要求 JSON 结构化输出再认真评估 SGLang 和 vLLM。两者怎么选我的倾向是新项目、长上下文密集、前缀共享明显的场景SGLang 值得优先验证HiCache 这类分层缓存带来的实际收益很容易测出来老项目已经在 vLLM 上稳定跑着也没必要为了追新而迁移线上稳定比什么都重要。5. 离线部署 Qwen3-8B 的完整实操记录接下来分享一份可以直接照着做的流程。机器环境是 Ubuntu 22.04、单张 RTX 4090、CUDA 12.x目的是完全离线跑通 Qwen3-8B并验证缓存命中带来的速度差异。5.1 环境准备与安装我建议用 conda 单独建一个环境避免污染系统 Python。SGLang 对 PyTorch 版本有要求最好是匹配官方验证过的组合省得后面出现莫名其妙的兼容问题。conda create -n sglang python3.10 -y conda activate sglang pip install --upgrade pip pip install sglang[all]安装完成后可以执行python -c import sglang; print(sglang.__version__)确认。如果安装很慢多换几次源不要死磕一个节点。5.2 先离线把模型文件备好离线部署最关键的一步先把模型完整下载到本地。千万别在有网环境里直接指向远程仓库路径然后期望着到离线环境里还能加载大概率会卡在联网检查上。我用的是 ModelScope 的下载命令速度对国内网络很友好一条命令拉到本地目录pip install modelscope modelscope download --model Qwen/Qwen3-8B --local_dir /data/models/Qwen3-8B下载完检查目录里应该有config.json、model.safetensors分片文件、tokenizer.json、tokenizer_config.json这些关键文件。Qwen3-8B 的 BF16 权重大约 16GB单张 24GB 显存的 4090 完全能装下扣除权重后还剩约 7GB 给 KV Cache 使用。5.3 启动 server 的完整命令离线环境下启动服务关键是指定本地模型路径并明确上下文长度和显存占用比例。我常用的命令长这样python -m sglang.launch_server \ --model-path /data/models/Qwen3-8B \ --host 0.0.0.0 \ --port 30000 \ --mem-fraction-static 0.85 \ --context-length 32768 \ --tp-size 1几个参数分别说明一下--mem-fraction-static给权重和静态图预留的显存比例剩下的是 KV Cache 增量空间。0.85 对单卡部署比较稳太高容易在长上下文时 OOM--context-length最大上下文长度Qwen3-8B 基础版是 32K 附近超长需求要配合推理扩展手段处理--tp-size张量并行卡数单卡就写 1这直接规避了上一章说的无 NVLink 通信问题。启动成功的标志是日志里出现类似“Server is ready”且没有显存分配失败的报错。5.4 客户端调用与缓存验证SGLang 提供 OpenAI 兼容接口用起来非常顺。离线环境里直接用本地地址调用from openai import OpenAI client OpenAI(base_urlhttp://localhost:30000/v1, api_keyEMPTY) resp client.chat.completions.create( modelQwen3-8B, messages[{role: user, content: 请用一句话介绍你自己}], ) print(resp.choices[0].message.content)这里我想额外强调一个很有用的缓存验证方法。先准备一段比较长的 system prompt比如一份 2000 字的知识库规则说明然后连续发起两次完全相同的请求。第一次因为冷启动TTFT 会有明显耗时第二次如果命中了前缀缓存TTFT 会大幅下降这个时间差就是缓存复用的直接证据。更系统一点的做法是访问http://localhost:30000/metrics在 Prometheus 格式的输出里找缓存相关的指标观察命中次数和不命中次数。我习惯先做一轮基线请求再修改 system prompt 里一个字符对比指标变化能直观看出前缀共享的边界。5.5 我踩过的几个坑离线部署总有几个坑是文档里不会写的我逐个说第一显存分配比例设置贪心。我一开始把--mem-fraction-static调到 0.95启动确实很快但一跑到长上下文直接 OOM。后来回到 0.85损失一点峰值吞吐换来整个服务的稳定性。KV Cache 是动态分配的必须给增量留足空间。第二本地模型目录缺文件。有一次我只拷贝了权重文件忘掉tokenizer_config.json启动后模型能加载但聊天模板完全错乱生成了一堆奇怪格式的回复。文件完整性检查别省。第三版本错配问题。SGLang 和 transformers 的版本如果没对齐可能出现一个请求发过去服务端报一堆和缓存复用相关的内部错误看起来像是缓存问题其实是版本不兼容。遇到诡异问题先查版本再查缓存。第四Qwen3 的 chat template 在部分旧版本里需要显式指定。如果回复风格不对或打印了原始模板占位符大概率是模板没配对启动参数里加对应模板名就行。写在最后这套方案跑通之后我最大的感受是缓存机制用得好可能比多买一张显卡还值。以前总觉得没 NVLink 就玩不了大模型多卡后来发现靠数据并行加分层缓存消费级显卡也能把 Qwen3-8B 这类模型服务得挺体面。如果你手头正好也是无 NVLink 的机器先别急着换硬件照着上面的思路把缓存和并行策略调一调说不定性能提升比预期大得多。