Skip to content

推理优化 · 技术问答

推理优化 · 技术问答

必会

重点 TTFT 过高怎么诊断和解决?

口述:TTFT 是发请求到第一个 token出来,主要卡在 Prefill。常见体感门槛大约 2 秒。先分清是算力被长 prompt 占满,还是 KV 爆了在换页。

怎么诊断

  1. prompt 很长且并发不低(经验:长度过 4k、同时十来个请求)→ Prefill Compute-Bound。一次把整段 prompt 算完会独占 GPU,别人的 Decode 只能等。
  2. KV Cache 占用已经很高(经验:超过约 0.9)→ Memory-Bound / Swapping。显存不够把 KV 踢到 CPU,首 token 被换页拖死。

怎么解决

  1. Chunked Prefill — 长 prompt 切成小块(常见 512 token)和 Decode 交错跑。像高速路分时:长途车跑一段就让道给短途车,短请求的 Decode 不用干等到 Prefill 整段结束。
  2. Prefix Caching — 系统 prompt、公共前缀的 KV 复用,别每个请求重算一遍。
  3. PD 分离 — Prefill 和 Decode 分卡或分实例,长 Prefill 不再堵住正在吐字的请求。

Chunked 有效,是因为它拆的是「独占 GPU 的那一次大 Prefill」,不是把模型变快。KV 已经在 swap,先减并发、量化 KV、或加卡,光切块救不了换页。

重点 PagedAttention、Continuous Batching、推测解码、PD 分离分别解决什么问题?

口述:四层,各打一个痛点。别说成互相替代——vLLM 里前两层几乎是标配,推测和大 Batch 会打架,PD 是流量大了再拆。

  1. PagedAttention(内存层) — 解决 KV 碎片和显存浪费。按最大长度连续预留会内部/外部碎片。KV 切成块、按需分配,像虚拟内存。
  2. Continuous Batching(调度层) — 解决 GPU 空等。静态 batch 要等最长的那条;改成每出一个 token 调度一次,谁完谁让位。
  3. 推测解码(算法层,EAGLE / Medusa) — 解决 Decode 生成太慢(一步一字、Memory-Bound、算力闲着)。草稿先猜一串,大模型一次验,少走生成步数。大 Batch 已经 Compute-Bound 时收益变小。
  4. PD 分离(架构层) — 解决 Prefill 和 Decode 互相抢。分到不同 GPU,P 盯 TTFT、D 盯 TPOT,硬件和 batch 可以分开设。

口条:分页省显存,持续批填满卡,推测少走步,分离别互抢。

重点 PagedAttention、Continuous Batching、推测解码、PD 分离如何叠加使用?

口述:不是四选一。PD 是最外层部署,决定另外三样跑在哪张卡上;里面 P、D 各自还有调度和分页。推测只挂在 Decode。

  1. PagedAttention — 基础设施,Prefill 和 Decode 两端都要,管 KV 怎么切块。不分 PD 也得开。
  2. Continuous Batching — P 池、D 池 各自跑:P 小 batch 插新 prompt;D 大 batch 调度正在生成的请求。拆开后不是关掉 CB,是两套队列。
  3. 推测解码只开在 Decode。Decode 才 Memory-Bound,才值得草稿+一次校验。Prefill 已经是大矩阵乘,再猜没有意义。大 Batch 饱和时减步数或关掉。
  4. PD 分离 — 最外层:P 集群(CB + PagedAttention)写出 KV,传到 D 集群(CB + PagedAttention + 推测)。

vLLM 里前两样默认就有;EAGLE 用 --speculative-model 指草稿(官方集成);Medusa 多是社区;PD 靠 KV Transfer Connector 配 P/D 角色。口条:分页打底,两边各自持续批,推测只给 Decode,PD 决定它们分家。

重点 为什么需要 PD 分离?

口述:问的是混合执行有什么问题。Prefill 和 Decode 在同一张 GPU 上交替跑,两段脾气相反,谁都跑不好——短跑和马拉松在同一条跑道交替训练。

  1. Prefill 堵住 Decode — Prefill 是计算密集(Compute-Bound)。它在跑的时候,延迟敏感的 Decode 插不进 → TPOT 飙升,用户觉得卡顿(字突然停住)。
  2. Decode 浪费 Prefill 的算力 — Decode 是访存密集(Memory-Bound)。它在跑的时候计算单元大量空闲,而这正是 Prefill 最缺的东西 → 算力白扔,新请求 TTFT 也上不去。

所以要拆开:P 池专门短跑(堆算力、盯 TTFT),D 池专门马拉松(堆带宽和显存、盯 TPOT)。Chunked Prefill 只是同一条跑道分时让道,还是会互相踩;要彻底不抢才 PD 分离。小流量、短 prompt 不必上。

为什么短 Prompt 的 TTFT 反而更高?

口述:多半不是 Prefill 变快了,是第一次请求在冷启动。测 TTFT / TPOT 必须 stream=True,每个 token 打时间戳;非流式只能拿到整段结束时间,分不开首字和后续间隔。

  1. 怎么测 — TTFT = 首 token − 发请求;TPOT = 相邻 token 间隔(看平均和 P95)。换短中长 prompt,本应看到 TTFT 随长度涨。
  2. 冷启动 — 第一次请求要 JIT 编译 CUDA kernel、分配显存,TTFT 可以虚高好几倍。示例:「你好」第一次 355ms,后面中长 prompt 40–63ms。
  3. 怎么避 — 压测和生产都先发几条预热,丢掉首轮再看分布。按 prompt 长度分桶,别让短句把 P99 淹掉。

Batch Size 大了,会减弱推测解码效果么?

口述:会。 Decode 一次前向像一趟车。车上的人 ≈ 当前 batch 里并行的请求。推测解码是额外再让草稿模型猜一批 token、大模型一次校验。车没坐满时,多算这点草稿能把算力吃满,加速明显;车已经挤满时,再塞草稿计算,边际收益很小,开销还可能拖后腿。类似拼车:空车拉人很划算,满员再塞人就不划算。

  1. Batch 小 → Decode 偏 Memory-Bound — 算力没吃满(车上 1 个人)。草稿那点额外计算正好填闲置算力,利用率上去,加速明显。
  2. Batch 已经很大 → GPU 接近 Compute-Bound — 算力快饱和(车上已挤 30 人)。再硬塞草稿前向,和大模型抢算力,加速比掉下来,草稿开销甚至拖慢。
  3. 怎么讲清 — 推测解码赚的是「一次大模型前向验多个 token」;前提是这次前向本来有余量。高并发、大 batch 时余量没了,效果自然减弱。生产上常对低 batch / 长尾请求开推测,满载时关或减小草稿长度。

并发压测怎么验证 Continuous Batching 生效了?

口述:别只报单请求 tokens/s。把并发从 1 扫到 16,看吞吐涨不涨、P50 动不动。生效时:车上人变多,这一趟时间差不多,运力翻倍。数字是示例,别说成自己测的。

怎么压

  1. 先预热,丢掉冷启动。prompt / 输出长度固定,并发 1、2、4、8、16 各跑一轮。
  2. 记 QPS、tok/s、P50 / P99(流式测延迟)。没有分位数,只报平均,看不出拐点。

怎么读

  1. 还在 Memory-Bound — 示例里并发 1→8,吞吐 101→635 tok/s(约 6 倍),P50 只从约 900ms 到 1037ms(约 +15%)。这就是 Continuous Batching:1 个请求和 8 个请求,GPU 这一步耗时差不多。
  2. 拐去 Compute-Bound — 并发到 16,P99 跳到约 2s。算力饱和,再加并发只排队,P50/P99 一起坏。
  3. 没生效 — 吞吐几乎不涨,或延迟和并发同比翻,说明还在齐步走的静态 batch,或 KV/调度已经卡住。

生产看拐点设 --max-num-seqs:拐点前吃吞吐,拐点后保尾延迟。

什么时候用 SGLang,什么时候用 vLLM?

口述:先问场景,不要先比 tokens/s。

  1. 简单问答 / 单轮 → vLLM。成熟、OpenAI 兼容;缓存也没啥可赚。
  2. 多轮 / 客服 → SGLang。Radix 吃历史前缀。
  3. 批量 / 同一模板 → 评估 SGLang。共享前缀可能降低重复 Prefill,但收益取决于命中率、模型、版本、并发和输入形状,必须压测。
  4. 严格 JSON / Agent → SGLang。约束解码保证合法。

已经跑着 vLLM、请求又散又短,不必迁。上线看 cache_hit_rate,接近 0 换引擎也没用。

口条:没前缀用 vLLM,有前缀或要硬 JSON 用 SGLang。

进阶

Medusa-1 和 Medusa-2 的区别是什么?

口述:都是给原模型加 Medusa Heads 做推测解码。差在骨干动不动。1 冻主干,质量对齐原模型;2 把头和主干一起训,头更准、更快,但要护住原能力。论文加速大约 2.2× vs 2.8×,别说成自己测的。

  1. Medusa-1(冻结主干) — 原 LLM 参数完全不动,只训新增的 Heads。生成无损,和原模型一致。训得便宜,适合不能改底座、要保证答案不变。
  2. Medusa-2(联合训练) — Heads 和主干一起微调,两边分布更齐,头猜得更准,一次收下更多 token。质量几乎无损,靠特殊训练技巧保住原能力;训坏了就不是原模型了。
  3. 怎么选 — 先问能不能动底座。不能动、要无损 → 1。能微调、要更高吞吐 → 2。加速比是论文量级,面试报区间、说清前提(Heads 准、树和 Typical Acceptance 配上)。

EAGLE-1、EAGLE-2、EAGLE-3 有什么区别?

口述:三代都是特征级推测,一代解决一个主要痛点。1 解决 token 预测不稳;2 解决静态树浪费;3 解决训练推理不一致和特征表达受限。论文指标只用于理解方向,不替代当前版本实测。

  1. EAGLE-1 — 特征级预测 + Shifted-Token(已采样的词提前一拍喂进去),降低离散 token 预测的不确定性。
  2. EAGLE-2 — 动态草稿树,按置信度长枝、剪枝,减少静态树的无效验证。
  3. EAGLE-3 — 通过训练时暴露预测误差和多层特征融合改善草稿质量。
  4. 大 Batch — 当 GPU 已接近计算饱和时,推测解码收益会下降;具体拐点必须在目标负载下测量。

PD 分离的原理是什么?

口述:请求先到 Router → Prefill 集群写出完整 KV → 高速网络把 KV 交给 Decode 集群 → 只在 D 上自回归吐字。两池硬件可以不一样。

  1. P 池(计算密集) — 全序列注意力,小 batch,适合 H100 这类高算力卡,盯 TTFT。一条长 prompt 已经是大矩阵乘,不需要再堆很多请求。
  2. KV 传输 — NVLink / RDMA / InfiniBand。传慢了,分离省下的首字延迟全没了。
  3. D 池(带宽密集) — 只 Decode,大 batch 把显存带宽吃满,可叠 EAGLE/Medusa;适合 A100 80G 这类大显存卡,盯 TPOT。

和混部的差别:混在一张卡上 Decode 开大 batch 会打爆正在吐字的 TPOT;分开后 D 的大 batch 堵不到别人的 Prefill。原理是 算力卡和带宽卡各干各的,中间只交 KV,不是简单多买几张同样的卡。

PD 分离的核心优势是什么?为什么 Prefill 小 Batch、Decode 大 Batch?

口述:四条——隔离、特化、独立扩、batch 也能分开设。别把 TPOT 说成首 token:稳住吐字的是 TPOT,首字是 TTFT。

  1. 消除干扰 — Prefill 不再堵住 Decode,TPOT 稳定。
  2. 资源特化 — P 用高算力卡,D 用大显存卡。
  3. 独立扩展 — 长 prompt 加 P,长生成加 D。
  4. Batching 差异化 — P 小 batch,D 大 batch。

小 / 大 batch 的原因

  • Prefill 整段矩阵乘已经 Compute-Bound,再加大 batch 只增加排队,不涨吞吐。
  • Decode 一步一字 Memory-Bound,加大 batch 把闲着的算力用上,往 Compute-Bound 靠,吞吐才上去。

混部不敢让 Decode 开大 batch;分开之后才敢。口条:小 batch 短跑、大 batch 马拉松

怎么理解 vLLM 用 KV Transfer Connector 做 PD 分离?

口述:前面说的「P 写出 KV、D 接着吐字」,在 vLLM 里靠 KV Transfer Connector 这一层把 KV 从 Prefill 实例搬到 Decode 实例。像饭店传菜:厨房不管上桌,服务员不管备料,中间有专用电梯。

  1. 客人点菜 = Prompt — 请求进 Prefill 池。
  2. 厨房 = Prefill — 切配、调汁,一次性备好全部食材,也就是整段 KV Cache
  3. 传菜电梯 = Connector — 常见 PyNcclConnector 走 NCCL,GPU 到 GPU 推 KV,不必经 CPU 绕一圈。厨房可以一口气备好多份(多条请求的 KV)再推。
  4. 服务员 = Decode — 一边收菜,一边一勺一勺盛给客人(逐个 token)。KV 到了就开始 Decode,不用在同一张卡上等厨房忙完。

Connector 可换:同机 / NCCL 用 PyNccl;跨机常用 RDMA 类实现(如 Mooncake)。传得比 Prefill 还慢,分离就亏了。面试别把 Connector 说成「又一种注意力」,它只是 PD 之间的 KV 搬运插件

vLLM 里 PD 分离具体怎么实现?

口述:配置里把实例标成生产者或消费者,靠 KV Transfer Connector 推 KV。不是再写一套引擎。

text
# P:kv_role=kv_producer, kv_rank=0
# D:kv_role=kv_consumer, kv_rank=1
# kv_connector 常见 PyNcclConnector
  1. 异步 — Prefill 算完就推,P 不用等 D 吐完。
  2. 零拷贝 — NCCL / NVLink / InfiniBand,GPU 直传。
  3. Router — 新请求去 P,不进 D。
  4. CB 仍在 — P、D 各自 Continuous Batching,只是队列分开了。

口条:producer 出菜,consumer 上桌,电梯是 Connector。

除了 vLLM,业界还有哪些 PD 分离实现?

口述:思路都是 P/D 分池、中间交 KV,差在调度和 KV 放哪。能报出三四家、说出一句定位就够,别装成自己都上过线。

  1. Mooncake(Kimi) — 以 KV Cache 为中心 的分离架构:KV 进一层可复用的存储/传输,不只是一次 P→D 的点对点拷贝。vLLM 也可以用 Mooncake 当 Connector。
  2. NVIDIA Dynamo — 声明式把集群拆成 VllmPrefillWorker + VllmDecodeWorker,引擎仍可以是 vLLM,Dynamo 管编排和流量怎么进 P/D。
  3. SGLang — 自己的推理栈里做 PD 分离,和 vLLM 是另一条引擎线。
  4. llm-d — Kubernetes 原生推理栈,把 PD 和路由落在 K8s 上,底层常接 vLLM 一类引擎。

面试一句:vLLM 是引擎 + Connector;Mooncake 把 KV 当一等资源;Dynamo / llm-d 偏平台怎么把 P、D 跑起来。选型先看已有引擎和要不要上 K8s,不是比谁名字新。

RadixAttention 是什么?解决什么问题?

口述:解决 前缀 KV 被反复重算。多轮、fork、同一段 system 本来算过一遍,不复用就每次 Prefill。PagedAttention 管怎么切块;RadixAttention 管跨请求哪一段能共享。

  1. 基数树 — token 前缀映射到 KV,快速查找 / 复用 / 插入 / 删除。像按「皇宫-早上」分类的道具架。
  2. LRU — 满了先丢最久不用的叶子,公共布景留下。
  3. 缓存感知调度 — 相似请求连着跑,提高命中。导演让同类戏连着拍。

比喻:第一场「皇宫-早上-宴会」道具搬齐;第二场「皇宫-早上-议政」只加书桌。没有这棵树,每场都从仓库搬全部道具。

口条:目录树找前缀,LRU 腾地方,调度让命中发生。不是新的 Attention 公式。

怎么理解基数树(Radix Tree)的数据结构细节?

口述:基数树是压缩过的前缀树。传统 Trie 一个字符一条边;Radix 把没有分叉的一串字符收成一条边。查得更快,节点更少。

用「北京市朝阳区」:

  1. Trie(逐字档案柜) — 北 → 京 → 市 → 朝 → 阳 → 区,六个抽屉,每个字单独开一层。没有分叉也要下一层。
  2. Radix(压缩文件夹) — 可以一条边直接标「北京市朝阳区」。若还有「北京市海淀区」,先一条边「北京市」(公共前缀),再分叉「朝阳区」/「海淀区」。

边里存的是一段序列,不是单字。查找顺着边比,遇到分叉才拆节点。LLM 里同样结构,只是边标的是 token 串 而不是汉字:公共 system / 历史走同一条边,KV 只存一份;从分叉点开始才是各请求自己的后缀。SGLang 的 RadixAttention 就是拿这棵树当前缀缓存的索引。

RadixAttention 的树、分页和 LRU 怎么配合?

口述:索引是压缩前缀树,存储是按 token 分页,淘汰 先丢叶子 才能保住公共祖先。

  1. 边是一段 token,不是一个字 — 相对 Trie 少很多层,长 system 收成一条边。
  2. KV 非连续分页 — 一页一个 token,按前缀切开共享,不必整段连续显存。
  3. 先逐出叶子 — 独有后缀先扔;「皇宫-早上」这种祖先只要还有孩子在用就留着。所有孩子没了,祖先才变成叶子,才轮到它。

口条:树负责找,页负责放,叶子先走、祖先后走。

缓存感知调度是什么?为什么不 FCFS?

口述:命中率 = 缓存中的 prompt token / 总 prompt token。队列顺序不对会 抖动:刚载入的前缀马上被换出。所以不按到达时间,按 和树上匹配的前缀长度,长的先跑。

机场安检:FCFS 是东京、纽约、东京、伦敦来回换设备;缓存感知是三个去东京的连着过,东京这套 KV 不用动。

一次调度:用基数树给等待请求匹配前缀 → 按长度排序 → 在显存预算内组 batch(不够就 LRU 丢叶子)→ 跑一步 → 完成的 KV 插回树。

口条:树回答「能复用多长」;调度保证「复用得上的人连着来」。

约束解码是什么?SGLang 的压缩 FSM 解决什么?

口述:把输出限制在正则 / JSON schema 里,非法 token 直接不能出。传统实现一步仍只解 1 个 token,"name": 这种确定片段也慢慢走,长 JSON 很亏。

SGLang 用 压缩有限状态机:连续合法且确定的多个 token 合成一步解码。状态机描述允许的转移,能合并就合并。

和 prompt「请输出 JSON」比是硬约束;和事后校验重试比少浪费。Radix 省前缀 Prefill,FSM 省格式 Decode。

口条:软提示会破格式;压缩 FSM 让非法路径在解码里就不存在,确定字段一次吐完。

压缩 FSM 具体怎么加速?用 {"summary": " 怎么讲?

口述:正则先变成 FSM。解码时看当前状态,非法 token 概率置零。普通做法一步一字,{"summary": " 要多次前向。压缩是把没有分叉的边收成一条,整段常量一次 Decode。

  1. 普通 (a)(c) — 十几个状态,每个字符/token 一次 LLM,哪怕后继只有一个合法选择。
  2. 压缩 (b)(d){ + "summary" + ": " 挂在同一条边上,一次前向吐完,模型只解后面真正不确定的摘要正文。
  3. 通用 — 对任意正则都能压,不是 JSON 特例。长约束、长固定骨架最赚。

口条:FSM 负责合法;压缩负责别为确定字符串反复调模型。

了解

Prometheus 指标怎么采集?

口述:vLLM 自己吐 Prometheus 文本,默认 http://localhost:8000/metrics。生产用 Prometheus 去 scrape、Grafana 看图;不要把作业里那段正则当成线上方案。

拉什么

  1. 打开 --enable-metrics(视版本也可能默认开),curl 一下 /metrics 确认有数。
  2. 先盯四个:prompt_tokens_totalgeneration_tokens_total(Counter,累计)、num_requests_running(Gauge,当前在跑)、KV 占用(名字可能是 gpu_kv_cache_usage_percent / gpu_cache_usage_perc,以端点为准)。
  3. 吞吐看两次采样的差值 / 时间:生成 token 累计 2 秒涨约 900,就是约 450 tok/s。不要拿 Counter 的绝对值当瞬时速度。

怎么读,别踩坑

  1. 负载前后对比 — 示例:20 并发后输入 +260、生成 +3000,说明请求真的打进引擎了。
  2. KV 显示 0% — 小模型 + 大显存很常见(示例 0.6B 配 24GB)。换成 7B/13B 或长上下文才会涨。不是采集失败。
  3. running 经常是 0 — Gauge 只反映采样那一瞬;请求在两次 scrape 之间跑完就记成 0。作业每 2 秒采一次会漏,生产 Grafana 按秒拉,并同时看 num_requests_waiting

日常看板用 Grafana;正则抠文本只适合本机确认端点活着。

LLM Inference 和 LLM Programs 分别是什么?

口述:Inference 是单次调用;Program 是用程序把多次调用串起来。应用已经从聊天变成后者。

  1. LLM Inference — Prompt 进、token 出。优化这一趟:KV、batch、推测、PD。这是 vLLM 的主场,让模型跑得快
  2. LLM Programs — 语言模型程序:代码里调度多次生成,中间有分支、循环、并行。两个属性:① 多调用 + 控制流(复杂任务、要质量);② 结构化输入输出(才能和其他软件拼接)。这是 SGLang 的主场,让业务逻辑跑得快

只做一问一答,Inference 够了。Agent、多轮工具、必须吐 JSON,瓶颈在前缀重算和重试,要按 Program 来优化,不是再把 tokens/s 抠一点。

SGLang 是什么?前后端各干什么?

口述:给 LLM Programs 用的语言 + 运行时。前端让你用 Python 写 gen / fork / select;解释器交给后端跑。后端三板斧:RadixAttention 复用 KV、压缩 FSM 做约束解码、纯 API 时推测执行少打调用。论文大约 6.4× 吞吐,别说自己测的。

  1. Frontend — 嵌在 Python 里的原语,管提示状态。function 定义函数;system/user/assistant/image 拼上下文;gen 生成;select 选项里挑;fork 并行分支;run 执行。
  2. 例子 — 多维论文评分:multi_dimensional_judge(s, path, essay),fork-join 把大问题拆小、并行打分再合并。s 是提示状态。
  3. Runtime — 看得见调用图,所以能自动共享前缀、按语法一次解多个 token。

和 vLLM 对照:vLLM 优化单次 Inference;SGLang 优化带控制流的程序。只聊天不必上这套 DSL。

SGLang 的编程特点是什么?

口述:看起来是 Python,跑起来被运行时改成并行和自动加速。和 LMQL / Guidance 一样能直接操作提示原语,但它自带 SRT 运行时

  1. if/for/return — 例如 if s["related"] == "no": return,按模型输出走分支,没有新模板语言。
  2. 隐式并行fork 写起来像循环,三个维度的评判会并行生成,RadixAttention 复用共同上下文。手写 Prompt 做不到。
  3. 解释器 / 编译器 — 解释器把提示当异步流,不必死等每一次生成;编译器变成计算图再优化。
  4. 自动加速 — KV 前缀复用;regex/schema 约束出合法 JSON;纯 OpenAI API 也能推测执行少打调用。

口条:你负责用 Python 写控制流,运行时负责并行和缓存。

针对非连续分页存储,如何处理?

口述:PagedAttention 把 KV 拆进很多不挨着的块,像档案散落在多个抽屉。基数树不当成「一整段连续内存」,而当索引卡:节点记下这条前缀对应哪些 Block ID,按序拼起来就是完整路径。

  1. — 树边/节点指向一串页号,不要求页在显存里连续。查找前缀 = 顺着树拿到页表,注意力按页去取。
  2. — 柜子满了先丢叶子:例如「朝阳区-某小区-具体门牌」这份具体档案。公共祖先「北京市朝阳区」留下,别人还可能走这条文件夹。
  3. 为什么先丢叶子 — 叶子只服务少数请求;祖先被很多分叉共享,丢掉就要大面积重做 Prefill。引用计数掉到 0 再真正还页。

口条:分页负责碎块怎么放,树负责路径怎么找;淘汰具体档案,保留共用文件夹。

针对 AI 客服,RadixAttention 怎么处理真实请求?

口述:用客服中心走一遍树的生长、拆分、淘汰。system 和 few-shot 是文件夹,每人的后文是叶子。

  1. 开张 — 树是空的。
  2. 第一位客人 Hello — Prefill「你是一个有帮助的助手」+ Hello,整条路径建成节点 a,KV 新建。
  3. 同一人续问 — 前缀已在树上(蓝节点),只算新的 "Solve this problem…",挂到节点 b。第一轮 KV 不重算
  4. 第二位客人 — 只该共享 system,不能共享第一位的 Hello。把原来那条边拆开:公共 system 当祖先,一人走 Hello 枝,一人走 "What can you do?" 枝。
  5. 显存紧 — LRU 先丢最久未用的叶子(第一人第二轮),刚用过的第二人第一轮留下,再在其下挂 "Write a story"。祖先 system 尽量留。
  6. 20 人同一套数学例题 — 和客服闲聊没有公共前缀,另开一条 few-shot 链;20 人共享例题 KV,只算各自的题。后来又一批同一例题:再拆节点,例题当文件夹,答案当叶子。
  7. 第一人回来 / 多样本 — 腾地方就踢最冷的客人整枝。同一问题要 3 个答案(What/When/How):问题前缀算一次,下面分叉多个叶子。

口条:命中就接着长,分叉就拆节点,满了丢叶子留文件夹,同类题共享例题。

API 推测执行是什么?什么时候用?

口述:给 GPT / Claude 黑盒 API 用的。看不到 KV,每次 gen() 就是一次付费调用。第一次生成时越过 stop 多吐一些,缓存下来匹配后面的 gen,猜中就少打一次接口。不是 EAGLE 那种 GPU 草稿校验。

例子:name: + gen + job: + gen 传统两次 API;推测后第一次连职业一起写出来,第二次可能省掉。

要用:商业 API + 字段很规整(name/job/age)+ 能接受约 10–20% token 浪费换延迟。
不要用:本地模型走 Radix 更香;if/else 多、下一跳猜不准。

挑战:猜错白花钱;多出来的 token 要能和后续原语对上。生产要么本地 + Radix,要么官方 Prompt Caching;结构规整的填表程序才值得 API 推测。

口条:第一次多写一点给后面的字段用;规律填表省 RTT,乱分支别开。

SGLang 运行时看哪些指标?cache_hit_rate 多少算正常?

口述:curl :8001/metrics。running / waiting 看堵没堵;cache_hit_rate 是 Radix 灵不灵。多轮常见 60–80%,共享模板 80–95%,纯随机单轮接近 0%——这时别说 SGLang 一定更快。waiting 和 hit_rate 一起看:排队还命中为 0,多半是请求形状不对,不是没开 Radix。

口条:命中率是灵魂;单轮散问低是正常。