☰
别只看吞吐和延迟:LLM 推理中的 Admission Control,才是 Goodput 优化的总开关
2026/10/12 13:21:06 网站建设 项目流程

很多团队做 LLM Serving 优化时,最先盯的是tokens/s、TTFT、P95 latency,然后去调batch size、KV Cache、量化、并行策略、Chunked Prefill,甚至更换 vLLM、SGLang、TensorRT-LLM。
这些工作当然重要,但如果系统缺少一层明确的Admission Control(准入控制),线上服务很容易在高峰期进入一种“GPU 很忙、用户很慢、吞吐看似不低、可交付结果反而下降”的状态。
真正决定服务质量的,往往不是“还能不能再塞进一个请求”,而是“什么请求应该进、什么时候进、以什么代价进”。
本文从 AI Infra 和 LLM Serving 的工程视角,系统拆解 Admission Control 与 Goodput 的关系:为什么只看吞吐会误判系统健康,为什么很多优化上线后反而更容易抖动,Admission Control 到底在控制什么,它与 continuous batching、Chunked Prefill、KV Cache 水位、SLO、队列公平性之间如何耦合,以及在线上与面试里该如何把这个主题讲清楚。

导读

如果把前几篇关于Chunked Prefill、KV Cache、MoE 路由抖动、显存碎片的文章连起来看,会发现它们都指向同一个更上层的问题:

  • GPU 资源并不是无限的
  • 用户请求不是同质的
  • 长短请求、交互请求、批处理请求会相互竞争
  • 系统不能只考虑“尽量跑满”,还要考虑“结果是否值得交付”

这正是 Admission Control 发挥作用的位置。

很多人以为 Admission Control 只是“队列满了就拒绝”,这其实把问题想简单了。
在真实 LLM Serving 系统里,Admission Control 更像一个资源治理层,它决定:

  • 当前系统还能不能接新请求
  • 接进来以后是否会拖垮已有请求
  • 哪类请求优先保交互体验
  • 长 prompt、长输出、多轮会话是否需要分级处理
  • 当前是该追求吞吐,还是该保住 goodput

如果你做过线上推理服务,就会知道:系统最危险的时候,往往不是 GPU 空闲,而是 GPU 过于繁忙但没有产生足够高价值输出的时候。


目录

  1. 先讲清楚:什么是 Goodput,为什么它比吞吐更重要
  2. 为什么很多 LLM 服务会陷入“越忙越差”的状态
  3. Admission Control 到底在控制什么
  4. 它为什么不是简单的排队或限流
  5. LLM Serving 里最常见的四类资源冲突
  6. 只看 tokens/s 为什么会误导系统优化
  7. Admission Control 的核心输入:你到底该看哪些信号
  8. 常见准入策略一:基于队列长度和并发上限
  9. 常见准入策略二:基于 KV Cache / 显存水位
  10. 常见准入策略三:区分 Prefill 与 Decode 的分级准入
  11. 常见准入策略四:按请求画像做分层治理
  12. 为什么 Admission Control 会和 Continuous Batching 强耦合
  13. 它和 Chunked Prefill、Prefix Cache、量化是什么关系
  14. 线上落地时最容易踩的几个坑
  15. 一套更实用的工程落地框架
  16. 监控、压测与排障时重点看什么
  17. 面试里怎么把 Admission Control 讲得像真的做过
  18. 总结

1. 先讲清楚:什么是 Goodput,为什么它比吞吐更重要

很多团队会把下面几个词混着用:

  • 吞吐:单位时间内处理了多少 token、多少请求
  • 延迟:一次请求完成或首 token 返回用了多久
  • 利用率:GPU 看起来有多忙
  • Goodput:在满足目标体验或 SLO 约束下,系统真正交付的有效工作量

这里最容易被低估的是 Goodput。

1.1 吞吐不等于有效交付

假设系统每秒能吐出很多 token,但:

  • 大量请求已经超出用户可接受的等待时间
  • 交互式请求被长请求堵住
  • 请求虽然没报错,但延迟已经超过产品 SLO
  • decode 速度严重抖动,用户体感很差

那么这些 token 从工程角度看并不都是“有价值产出”。

也就是说,Goodput 关注的是“满足目标约束的有效吞吐”,而不是裸吞吐。

1.2 Goodput 更贴近真实线上目标

对一个聊天服务来说,用户关心的不是 GPU 是否满载,而是:

  • 能不能尽快看到首 token
  • 流式输出是否稳定
  • 高峰期是否明显卡住
  • 长问题会不会拖垮短问题

对平台或业务侧来说,关心的是:

  • 同样的 GPU 预算能稳定支撑多少在线并发
  • P95/P99 是否可控
  • 拒绝率是否合理
  • 是否能对不同租户、不同模型、不同流量类型做隔离

这些目标最终都会落到 Goodput,而不是简单的 tokens/s。

1.3 一个更工程化的理解

可以把 Goodput 理解成:

在既定 SLO、显存预算、GPU 算力、业务优先级和用户体验约束下,系统每单位时间真正交付的“可接受请求量”或“可接受 token 量”。

所以,Admission Control 的根本目标不是拒绝请求,而是保护 Goodput。


2. 为什么很多 LLM 服务会陷入“越忙越差”的状态

LLM 服务与传统 Web 服务不同,它不是“接到请求就简单算一下”。
它的资源竞争具有几个很强的系统特征:

  • Prefill 和 Decode 的负载形态不同
  • 长短请求差异极大
  • KV Cache 占用会持续累积
  • Continuous Batching 会让请求互相影响
  • 流式输出让单个请求生命周期变长

这会导致一种典型现象:

  • 低负载时,一切指标都很好
  • 中等负载时,吞吐持续上升
  • 再往上压时,GPU 利用率更高了,但 TTFT、TPOT、P99 延迟、取消率、超时率一起恶化
  • 继续压测,系统表面还在“工作”,但有效交付急剧下降

这就是典型的过载区间。

2.1 过载不一定表现为崩溃

很多工程师一开始会把“系统没挂”当成“还能接流量”。
但对 LLM Serving 来说,更常见的问题不是直接 500,而是:

  • 用户等到了,但等太久
  • 流式回复开始得慢
  • 回复过程中明显顿挫
  • 某些请求长期排队后被取消
  • 长请求吞掉了系统公平性

这时候系统没有宕机,但 Goodput 已经明显下降。

2.2 资源过载的后果会被 batching 放大

传统请求通常更独立,而 LLM 推理为了提高利用率,会主动把请求合在一起。
合批能提高效率,但也意味着:

  • 一个异常长的 prefill 会影响同一批次中的其他请求
  • 显存紧张会影响新旧请求的共存
  • decode 阶段如果被长 prefill 频繁打断,用户体感会快速恶化

所以越是高并发系统,越需要 Admission Control 做“进场秩序维护”。


3. Admission Control 到底在控制什么

很多人听到 Admission Control,第一反应是:

  • 请求进来,判断满不满
  • 满了就拒绝

这太窄了。

在 LLM Serving 场景里,Admission Control 至少在控制下面几件事:

3.1 控制系统进入过载区

核心问题不是“还跑不跑得动”,而是“再接这个请求之后,系统整体质量会不会下降到不可接受”。

3.2 控制不同请求之间的相互伤害

Admission Control 不是只看总量,还要看请求结构:

  • 短对话请求
  • 超长上下文请求
  • 长输出请求
  • 批处理离线请求
  • 高优先级租户请求

这些请求对系统的破坏力并不一样。

3.3 控制资源分配的形状

系统不是只有“算力够不够”的问题,还包括:

  • KV Cache 水位是否接近危险区
  • Prefill 是否会长时间霸占算力
  • Decode 是否还能维持平滑输出
  • 是否要给某类请求预留资源池

3.4 控制体验与成本的平衡点

Admission Control 的结果不只影响体验,也影响成本:

  • 放太松:系统进入抖动区,实际有效交付下降
  • 放太紧:GPU 利用率低,成本浪费

所以它本质上是在找一个最优工作区间。


4. 它为什么不是简单的排队或限流

排队、限流、Admission Control 很容易被混为一谈,但它们不完全一样。

4.1 限流更像外层保护

比如:

  • 每秒最多接收多少请求
  • 某租户 QPS 上限是多少
  • 某接口突发峰值限制是多少

这更偏入口层。

4.2 排队更像时间上的缓冲

请求先放进等待队列,等资源出来再调度。
它解决的是“现在不能立刻处理,但也许稍后能处理”。

4.3 Admission Control 是资源和价值判断

Admission Control 要回答的问题更强:

  • 这个请求是否值得进入系统
  • 它什么时候进入最合适
  • 它会不会伤害当前更有价值的请求
  • 它是否该降级、拆分、转移、延迟或直接拒绝

所以它经常结合:

  • 队列
  • 优先级
  • 资源预算
  • SLO 约束
  • 请求画像
  • 模型侧特征

它比简单限流和排队更接近“在线资源调度策略”。


5. LLM Serving 里最常见的四类资源冲突

理解 Admission Control,先要理解它在防什么。

5.1 Prefill 与 Decode 的冲突

Prefill 更偏大块计算,Decode 更偏小步迭代和连续响应。
如果系统为了拉高吞吐,放进太多长 prefill:

  • 短请求 TTFT 会恶化
  • 现有会话的 decode 会被打断
  • 交互式体验显著变差

5.2 长上下文与高并发的冲突

长上下文请求不仅 prefill 重,还会迅速抬高 KV Cache 占用。
一旦系统让太多长上下文会话同时入场:

  • 显存水位会快速上升
  • 可接入的新请求数下降
  • eviction 或回收压力增加

5.3 长输出与公平性的冲突

有些请求 prompt 不长,但max_new_tokens很大。
这类请求会在 decode 阶段长期占据系统资源,影响其他短请求完成。

5.4 在线交互与离线批处理的冲突

如果同一个服务同时承载:

  • 面向用户的对话流量
  • 后台批量生成流量

那 Admission Control 不做分层,几乎一定会互相污染。


6. 只看 tokens/s 为什么会误导系统优化

这是一个非常典型的线上误区。

6.1 tokens/s 会掩盖坏请求的存在

系统可能通过放入更多长输出请求,让总 tokens/s 看起来更高。
但与此同时:

  • 用户平均等待变长
  • 首 token 变慢
  • 短请求完成率下降

结果是吞吐好看了,Goodput 变差了。

6.2 GPU 利用率高不代表系统健康

GPU 一直满负荷,可能有两种解释:

  • 资源利用得很充分,系统处在良好区间
  • 资源已经被低价值工作塞满,系统处于过载边缘

如果不结合:

  • SLO 达成率
  • 请求取消率
  • 队列等待时间
  • KV Cache 水位
  • 各类请求的完成质量

单看 GPU Busy 很容易做出错误决策。

6.3 只优化 benchmark 容易把线上带偏

很多 benchmark 是:

  • 固定输入长度
  • 固定输出长度
  • 纯同质请求
  • 不考虑真实取消、超时、突发流量

这类 benchmark 很适合测底层引擎上限,但不等于线上系统最优。
Admission Control 正是在“benchmark 最优”和“线上最优”之间补上那层治理逻辑。


7. Admission Control 的核心输入:你到底该看哪些信号

如果只看队列长度,策略会非常粗糙。
更实用的做法是把 Admission Control 建立在一组组合信号之上。

7.1 队列类信号

  • 当前等待请求数
  • 等待时间分布
  • 各优先级队列长度
  • 某类请求是否出现堆积

7.2 运行时负载信号

  • 当前活跃会话数
  • 当前 decode token budget
  • 当前 prefill token budget
  • 每轮调度可用 slot 数

7.3 显存与 KV 相关信号

  • KV Cache 使用量
  • 可分配 block 数
  • 显存碎片程度
  • eviction 频率
  • swap/offload 是否触发

7.4 SLO 相关信号

  • TTFT P50/P95/P99
  • TPOT 或 decode token latency
  • 请求完成率
  • 用户主动取消率
  • 超时率

7.5 请求画像信号

  • 输入长度
  • 预估输出长度
  • 租户优先级
  • 是否交互式请求
  • 是否有 cache 命中潜力

真正成熟的 Admission Control 往往不是单阈值,而是多信号联合判断。


8. 常见准入策略一:基于队列长度和并发上限

这是最容易落地的第一层策略。

8.1 做法很直接

例如:

  • 活跃请求数超过阈值,不再接受新请求
  • 等待队列超过上限,直接拒绝或返回稍后重试
  • 某优先级队列超长时,低优先级请求限流

8.2 优点

  • 实现简单
  • 能快速防止请求无上限堆积
  • 对入口保护很有效

8.3 问题

它没有理解请求之间的异质性。
十个短请求和十个超长请求,对系统压力完全不同。
如果 Admission Control 只看“请求数”,会出现两类问题:

  • 明明还能处理一批短请求,却被一刀切拒掉
  • 明明系统已经很危险,只因为请求数不多还继续放行

所以这类策略适合作为第一道防线,但远远不够。


9. 常见准入策略二:基于 KV Cache / 显存水位

这类策略更贴近 LLM Serving 的真实瓶颈。

9.1 为什么 KV 水位特别关键

对于长会话或高并发场景,真正把系统推向危险区的,往往不是算力,而是 KV Cache。

因为:

  • 每个活跃会话都在持续占用 KV
  • 上下文越长,占用越大
  • 长输出也会让会话生命周期更长
  • 显存一旦逼近危险区,系统会迅速丧失弹性

9.2 基于水位做准入判断

例如可以设定:

  • KV block 使用率低于 70%:正常接入
  • 70% 到 85%:谨慎接入,只放行短请求或高优先级请求
  • 85% 以上:暂停长上下文请求,优先保 decode
  • 90% 以上:进入保护模式,只保已有会话和关键流量

9.3 这类策略的价值

它能避免“系统看起来还有算力,但其实已经没有足够显存余量”的误判。

9.4 但它也有边界

如果只看显存,会忽略:

  • decode 已经被长 prefill 压住
  • 队列等待已经过长
  • 某租户独占过多资源

所以仍需要与其他信号联合使用。


10. 常见准入策略三:区分 Prefill 与 Decode 的分级准入

这是 LLM Serving 里非常有代表性的工程策略。

10.1 为什么要区分

Prefill 和 Decode 对用户价值不同:

  • Decode 往往对应已开始响应的在线会话,打断体验最明显
  • 新的长 prefill 虽然重要,但优先级未必总高于已有 decode

因此很多系统会做:

  • 优先保 decode
  • 谨慎接入长 prefill

10.2 典型策略

  • decode 请求默认优先继续执行
  • 新进入的长 prefill 需要通过更严格准入门槛
  • 当系统进入高水位区时,只允许短 prefill 或高优先级 prefill
  • 超长 prompt 请求改走 chunked prefill 或延迟入场

10.3 它为什么有效

因为它保护了流式连续性。
很多用户对 TTFT 敏感,但对“开始输出后是否持续平滑”同样敏感。
如果 decode 经常被打断,用户体感会很差。

10.4 它的代价

如果过度偏向 decode:

  • 新请求进入速度会变慢
  • 长请求可能长期饥饿
  • 队列公平性变差

所以需要在交互体验和请求公平性之间找平衡。


11. 常见准入策略四:按请求画像做分层治理

这是更接近生产系统的做法。

11.1 不同请求应该区别对待

可以按这些维度分层:

  • 交互式 vs 离线批处理
  • 短输入 vs 长输入
  • 短输出 vs 长输出
  • 高优先级租户 vs 普通租户
  • 付费流量 vs 内部测试流量

11.2 常见治理手段

  • 不同队列
  • 不同 token budget
  • 不同等待上限
  • 不同拒绝阈值
  • 不同模型实例池

11.3 一个典型例子

比如把请求分成三类:

  • A 类:在线短对话,严保 TTFT 和流式稳定性
  • B 类:普通对话,请求量大但优先级中等
  • C 类:离线长生成,可容忍等待

那么 Admission Control 就不应该统一处理,而应该:

  • A 类优先保
  • B 类在中等负载下正常接入
  • C 类在高峰期被延迟、限流或迁移

这类分层对 Goodput 的改善通常比单纯再调一点 kernel 更直接。


12. 为什么 Admission Control 会和 Continuous Batching 强耦合

很多人把 Continuous Batching 当成单纯的“吞吐优化手段”,但从系统角度看,它会直接改变准入逻辑。

12.1 合批意味着请求相互影响

请求一旦共享 batch:

  • 其中一个长请求会改变这一轮的资源占用
  • batch 结构会影响 decode 调度节奏
  • 显存分配也会被整批形态影响

所以 Admission Control 不能只看“单请求能不能跑”,而要看:

  • 这个请求加入后,batch 会变成什么形状
  • 会不会拉坏当前 decode 节奏
  • 会不会把显存带入危险区

12.2 合批最怕错误的异质混合

理论上批越大越高效,但真实系统里:

  • 太长的 prompt 混进在线短对话 batch,不一定划算
  • 太长的输出请求混进短输出请求,会拖慢整体完成节奏

Admission Control 的任务之一,就是避免“看起来是合批,实际上是相互拖累”的组合。


13. 它和 Chunked Prefill、Prefix Cache、量化是什么关系

这些优化都不是孤立存在的。

13.1 和 Chunked Prefill 的关系

Chunked Prefill 的作用,是把长 prefill 变成可插队、可切分的资源占用形状。
它会让 Admission Control 有更多调度空间。

但要注意:

  • 有了 chunking,不代表可以无限放长请求
  • chunk 太碎会增加调度开销
  • 如果 Admission Control 不配合,长请求仍可能挤爆系统

13.2 和 Prefix Cache 的关系

Prefix Cache 能降低部分 prefill 成本。
这意味着:

  • 有高命中潜力的请求,准入代价更低
  • 低命中、超长、结构不稳定的请求,准入风险更高

因此更精细的 Admission Control 会把“是否可能命中缓存”纳入判断。

13.3 和量化的关系

量化通常会:

  • 降低权重占用
  • 改善吞吐或可部署规模
  • 给 KV 或并发留下更多空间

但这并不意味着 Admission Control 可以不做。
因为量化缓解的是资源压力,不是消灭资源竞争。
系统进入过载区的边界可能被推远,但不会消失。


14. 线上落地时最容易踩的几个坑

14.1 把拒绝率最小化当成目标

很多团队会天然觉得:

  • 拒绝越少越好

但在线服务里,合理拒绝往往比错误接入更健康。
如果系统接了太多请求,最后让大量请求超时、卡顿、取消,表面上拒绝率低,实则 Goodput 更差。

14.2 单阈值策略抖动严重

比如:

  • 显存超过 85% 就拒绝
  • 低于 85% 就全放

这种策略容易在阈值附近来回震荡。
更实用的是:

  • 滞回区间
  • 分级模式
  • 平滑窗口统计

14.3 只做全局准入,不做分类治理

结果往往是:

  • 某类重请求拖垮所有流量
  • 高优先级业务没有真正被保护

14.4 准入和调度脱节

如果 Admission Control 不知道调度器当前状态,只在入口层拍脑袋判断,策略会很粗糙。
成熟系统通常让准入层能看到:

  • scheduler backlog
  • active sequence 数量
  • token budget 使用情况
  • KV block 可用情况

14.5 只在压测数据上做阈值

线上流量通常更脏:

  • 输入长度分布更散
  • 输出长度更不可控
  • 取消率更高
  • 租户行为差异更大

所以阈值不能只从理想 benchmark 得出。


15. 一套更实用的工程落地框架

如果让我从 0 到 1 设计一套 LLM Serving 的 Admission Control,我会按下面五层来做。

15.1 第一层:外层流量保护

  • API 网关限流
  • 租户级配额
  • 基础并发上限

作用是防止异常流量把后端瞬间打穿。

15.2 第二层:请求分类

进入服务后先做轻量画像:

  • prompt 长度区间
  • max_new_tokens 区间
  • 用户/租户优先级
  • 在线还是离线

先分类,再决定进入哪个队列。

15.3 第三层:运行时资源判断

结合当前:

  • KV Cache 水位
  • 活跃 decode 数
  • prefill backlog
  • 预计 token budget
  • 最近一段时间的 TTFT / TPOT

判断是否允许进入。

15.4 第四层:准入动作不是只有“过/不过”

一个成熟系统里,准入动作至少可以有:

  • 立即接入
  • 延迟接入
  • 降级接入
  • 分块接入
  • 转移到低优先级实例池
  • 明确拒绝

15.5 第五层:闭环调参与回灌

上线后必须持续回看:

  • 哪类请求被拒得最多
  • 哪类请求造成过载最多
  • 拒绝是否真的提升了 Goodput
  • 是否存在饥饿或不公平

否则 Admission Control 很容易变成一堆静态阈值。


16. 监控、压测与排障时重点看什么

如果你怀疑系统已经进入“忙但不值”的状态,建议重点看下面几组指标。

16.1 用户体验指标

  • TTFT P50/P95/P99
  • TPOT 或每 token 解码延迟
  • 流式中断/卡顿比例
  • 请求取消率
  • 请求超时率

16.2 系统负载指标

  • 活跃 sequence 数
  • prefill 队列长度
  • decode 队列长度
  • 每轮调度 token budget
  • GPU Busy 与 SM 利用率

16.3 显存相关指标

  • KV Cache 占用率
  • 可用 block 数量
  • eviction 次数
  • swap/offload 次数
  • 显存碎片趋势

16.4 准入效果指标

  • 不同类别请求的接入率
  • 延迟接入比例
  • 拒绝率
  • 被拒后重试成功率
  • 分层队列公平性

16.5 一个排障判断思路

如果你发现:

  • tokens/s 没怎么掉
  • 但 TTFT、P99、取消率明显恶化

优先怀疑:

  • Admission Control 过松
  • 长 prefill 混入过多
  • decode 连续性被破坏
  • KV 水位过高后仍在盲目接入

这类问题不能只靠继续调算子解决。


17. 面试里怎么把 Admission Control 讲得像真的做过

这是一个非常适合拉开差距的话题,因为它天然带工程味。

17.1 不要只说“限流和排队”

如果只说:

  • 系统忙了就限流
  • 队列满了就拒绝

面试官会觉得你停留在通用后端思维。

17.2 要主动讲 LLM Serving 的特殊性

你应该强调:

  • 请求异质性强
  • Prefill / Decode 资源形态不同
  • KV Cache 会持续累积
  • Continuous Batching 会放大请求之间的干扰
  • 优化目标不是裸吞吐,而是 Goodput

17.3 一个更像实战的表达模板

你可以这样回答:

我理解 LLM Serving 里的 Admission Control 不是简单限流,而是基于运行时资源和请求画像的准入治理。因为大模型请求异质性很强,长 prompt、长输出、多轮会话会对系统造成完全不同的压力。
如果只看 QPS 或 tokens/s,很容易把系统推入过载区,看起来 GPU 很忙,但 TTFT、P99 和取消率都在恶化。
所以我更倾向把 Admission Control 建在多信号之上,比如 KV Cache 水位、活跃 decode 数、prefill backlog、请求长度分层和优先级,再结合 continuous batching 的调度状态决定是立即接入、延迟、分块还是拒绝。
核心目标不是少拒绝,而是保护在 SLO 约束下的 goodput。

17.4 如果面试官继续追问

可以继续展开:

  • 为什么长 prefill 会伤害 decode 连续性
  • 为什么合理拒绝比错误接入更健康
  • 为什么显存高水位时系统弹性会迅速变差
  • 为什么 batch 更大不一定更好

只要能把这些关系说顺,基本就不像背概念。


18. 总结

很多人做 LLM Serving,会把精力集中在:

  • 更快的 kernel
  • 更高的 tokens/s
  • 更大的 batch
  • 更激进的并发

这些都对,但都不够。

因为线上系统不是比谁把 GPU 塞得更满,而是比谁能在真实流量、真实约束、真实波动下,持续交付更高的 Goodput。

从这个角度看,Admission Control 的价值非常明确:

  • 它决定系统是否进入过载区
  • 它决定长短请求如何共存
  • 它决定交互体验是否能被保住
  • 它决定你做的那些底层优化,最终能不能转化成真实线上收益

一句话总结:

在 LLM 推理系统里,吞吐优化解决的是“能跑多快”,而 Admission Control 解决的是“该不该这样跑”。真正稳定的 Goodput,必须靠两者一起完成。

如果你正在搭建 AI Infra / LLM Serving 系统,建议把 Admission Control 从“后面再补”的边角逻辑,提升成和 KV Cache、batching、调度器同级的一等公民。
因为很多系统的上限,不是死在算子不够快,而是死在资源治理不够清楚。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询