资讯动态

vLLM 请求调度一篇讲透:高并发下如何平衡吞吐量与延迟

发布时间:2026/9/13 7:51:09 来源:尧图企业网站定制
vLLM 请求调度一篇讲透高并发下如何平衡吞吐量与延迟【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllmvLLM 是一个以高吞吐、低显存著称的 LLM 推理引擎它的请求调度是整个引擎的性能中枢连续批处理、分块预填充、抢占式的 GPU 资源分配都发生在调度器这一步。这篇只讲三件事调度器在管什么、优先级怎么排、显存不够时它怎么救场最后给你能直接抄的参数配方。为什么 LLM 服务需要专门的调度器想象一个场景100 个用户同时向你的推理服务提问GPU 只有一块。此时吞吐量单位时间处理的请求数和延迟每个请求从发起到拿到首 token 的时间是打架的让一个长 prompt 一口气跑完 prefill其他 99 个用户的首 token 就得干等反过来每步只让每个请求算一点点单个请求的延迟又被拉长。传统服务框架用静态批处理凑满一批全部跑完再开下一批。批次里哪怕有请求提前结束GPU 也得空转到批次收尾。vLLM 的连续批处理把粒度压到了每一步说白了完成的请求每步立即离场新请求立即补进来GPU 几乎没有空转。代价就是每一步都要重新算一遍这一批放谁、各放多少 token——这件事必须由一个专门的调度器来做这正是 Scheduler 存在的理由。vLLM 调度器里到底在管什么vLLM 请求调度围绕三个对象展开关系如下调度器维护 waiting等待和 running运行两个队列每个 step 决定处理哪些请求、每个请求推进一步多少 token。KV 块管理器KV 缓存即模型记住前文内容的笔记本kv_cache_manager.py把显存切成固定大小的块跟踪块被哪个请求占用并支持相同前缀的请求直接复用已算好的块省掉重复计算。块池block_pool.py维护空闲块队列和 LRU 驱逐顺序是最后一块砖从哪拆的决策者。请求的生命周期可以浓缩成一张状态图这里有个坑vLLM 的 V1 引擎默认走重算式抢占——显存不够时牺牲一个请求、丢它的 KV 缓存、移回等待队列而不是把 KV 缓存搬到 CPU 内存再换回来老版 V0 才默认交换式。所以图里没有 SWAPPED 状态如果你按老教程找它会找不到的。请求进来了vLLM 怎么排优先级排优先级其实是三层机制在协同。第一层是队列出队顺序由scheduling_policy控制取值fcfs默认先来先服务或priority按请求携带的优先级数值插队。第二层是 step 内的处理顺序每个 step 先照顾 running 队列里的请求decode 的 token 优先拿到预算再把剩余额度分给 waiting 里捞上来的新请求。第三层是 token 预算两个参数一句话版本max_num_batched_tokens单个 step 允许处理的 token 总预算默认 2048直接决定吞吐与延迟的天平倒向哪边max_num_seqs单个 step 最多并发多少个请求默认 128决定批的宽度。真正让长短请求和平共处的机制是分块预填充chunked prefill长 prompt 不再一口气 prefill 独占 GPU而是每步只推进一小段和其他请求的 decode 混在同一个批次里算。配套参数long_prefill_token_threshold给长请求单步推进的 token 数设上限防止一条超长 prompt 把整个 step 的预算吃光把其他请求的首 token 延迟顶爆。从源码注释看V1 调度器内部甚至没有独立的 prefill/decode 阶段每个请求只记一个已计算 token 数调度器把预算分给还没追平的请求。这个统一视角让分块预填充、前缀缓存、投机解码全部天然兼容不用为每种特性写特殊分支。GPU 显存不够时vLLM 会做什么KV 缓存被切成固定大小的块默认 16 或 32 个 token 一块请求的块表维护着逻辑位置到物理块的映射。vLLM 的 GPU 资源分配本质上就是对这些块做分配、复用和驱逐三件事。前缀缓存复用是块管理最大的收益来源相同前缀的请求直接挂载已缓存的块多轮对话、Agent、RAG 这类场景命中率通常很高。块池按 LRU 顺序管理空闲块带哈希的已缓存块排到队尾保留久一点无哈希的普通空闲块排队首优先被复用。当新请求来了而空闲块不够就从队头驱逐最久没被用过的缓存块腾位置——这个动作叫缓存驱逐频繁发生就说明缓存命中率在往下掉。水位机制则预留一小撮块作缓冲避免每分配一次都触发一次驱逐的抖动。预留太少显存贴边运行、抖动频繁预留太多白白浪费容量。抢占发生时被牺牲的请求有两类退路交换式SWAP把它的 KV 块逐块搬到 CPU 内存资源空出来后搬回去续跑。省的是算力代价是内存带宽和一笔 CPU 内存占用KV 越长搬运越贵。重算式RECOMPUTE直接释放 KV 块请求回等待队列资源空出来时从零重新 prefill。省 CPU 内存代价是白算一遍请求已经生成了上千 token 时心疼但胜在简单、不依赖主机内存。资源流向一张图说清V1 引擎默认只用重算式显存不够就从 running 队列尾部挑一个牺牲者释放它的块、移回 waiting 重新排队。抢占次数是一个值得盯紧的信号——偶尔发生正常持续发生说明容量根本不够该扩容或限流而不是继续拧参数。落地调优三个场景的参数配方下面这张表覆盖三类典型负载参数取值都给了方向性理由实际压测时以你的硬件为准参数高并发短问答长文档生成混合负载max_num_batched_tokens8192拉高多请求同跑4096压低保护长请求8192max_num_seqs256吃满并发64长请求占块多别贪128block_size32块少管理开销低16碎片小长文本友好16enable_prefix_caching开多轮对话命中高开长前缀复用省算力开long_prefill_token_threshold0短 prompt 用不上1024长 prefill 分块限速512中间值两边都不难受max_num_queued_reqs512防洪峰压垮队列64长请求排队成本太高256三个踩坑提醒各一句短问答别把max_num_batched_tokens一路拉到 16384首 token 延迟会跟着水涨船高TTFT 和吞吐是拿一个换另一个。长文档并发一高显存先爆抢占风暴比慢更难看max_num_seqs宁小勿大。混合负载long_prefill_token_threshold设小了长请求卡住设大了短请求陪跑先用 512 起调再按监控微调。怎么判断调度是否健康参数调完不看指标等于盲调。四个指标按优先级排前缀缓存命中率低于 10% 说明负载前缀差异太大调参空间有限该考虑请求侧路由按前缀分组打到同一实例高于 50% 说明命中良好。累计抢占次数健康状态应接近 0。持续上涨说明容量不足往扩容、降max_num_seqs或收紧max_num_queued_reqs方向调而不是加大 token 预算。运行中请求数长期贴着max_num_seqs不动说明批被卡宽了往上加并发。等待队列长度持续正增长说明吞吐跟不上到达速率先加max_num_batched_tokens再加实例队列长期为空则说明容量冗余可以降配。写在最后调度是 vLLM 性能的核心杠杆连续批处理决定 GPU 利用率分块预填充守住延迟下限抢占与块池兜住显存边界三者拧在一起才是完整的推理服务调优。建议的下一步很朴素先用默认配置压一遍记录 TTFT、TPOT 和吞吐的 baseline然后每次只改一个参数、用相同负载重跑对比——参数之间是耦合的单看某一个的直觉收益多半会被其他参数吃掉。【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价