很多团队做 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 过于繁忙但没有产生足够高价值输出的时候。
目录
- 先讲清楚:什么是 Goodput,为什么它比吞吐更重要
- 为什么很多 LLM 服务会陷入“越忙越差”的状态
- Admission Control 到底在控制什么
- 它为什么不是简单的排队或限流
- LLM Serving 里最常见的四类资源冲突
- 只看 tokens/s 为什么会误导系统优化
- Admission Control 的核心输入:你到底该看哪些信号
- 常见准入策略一:基于队列长度和并发上限
- 常见准入策略二:基于 KV Cache / 显存水位
- 常见准入策略三:区分 Prefill 与 Decode 的分级准入
- 常见准入策略四:按请求画像做分层治理
- 为什么 Admission Control 会和 Continuous Batching 强耦合
- 它和 Chunked Prefill、Prefix Cache、量化是什么关系
- 线上落地时最容易踩的几个坑
- 一套更实用的工程落地框架
- 监控、压测与排障时重点看什么
- 面试里怎么把 Admission Control 讲得像真的做过
- 总结
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、调度器同级的一等公民。
因为很多系统的上限,不是死在算子不够快,而是死在资源治理不够清楚。