调用方式
一、vLLM 提供的两种调用方式
1. Offline Batched Inference(离线批处理)
-
调用特点:一次性传入一批(batch)的请求,等待所有请求都处理完毕后,一次性返回推理结果。对用户而言,这是一个同步的过程:只有当这一批所有数据都推理完成,用户才能拿到结果。
-
示例代码(基于vLLM官方示例):
from vllm import LLM, SamplingParams # 1) 组织batch数据 prompts = [ "Hello, my name is", "The president of the United States is", "The capital of France is", "The future of AI is", ] # 2) 采样参数 sampling_params = SamplingParams(temperature=0.8, top_p=0.95) # 3) 初始化离线批处理实例,并加载指定模型 llm = LLM(model="facebook/opt-125m") # 4) 推理 outputs = llm.generate(prompts, sampling_params) # 5) 打印结果 for output in outputs: print(f"Prompt: {output.prompt!r}, Generated text: {output.outputs[0].text!r}")
-
背后原理:
- 表面上是同步:用户会一次性提交一批请求(batch),等待所有请求都推理完成再返回。
- 实际上是“动态”:vLLM的核心引擎(LLMEngine)内部会根据当前GPU显存的使用情况,对batch做“动态”调度。在一个推理阶段(prefill或一次decode)里,如果显存无法容纳所有请求,vLLM会先让部分请求进入“running队列”,剩下的请求留在“waiting队列”里等待。
- 多阶段推理:当某些请求在一次推理阶段完成或结束(生成了结束符或达到最大长度),它们就会释放所占用的显存块。这样下一批等待的请求就能进入“running队列”,继续下一阶段推理。
- 对用户而言是透明的:用户只看到自己提交了一个batch,等所有结果都处理完后才拿到输出;但在内核引擎中,这个batch可能被分拆成多个子批次进行推理。
2. API Server For Online Serving(在线服务)
-
调用特点:采用异步方式处理请求,随时可以接收新请求、并行处理多个请求;已完成的推理结果可以先行返回给客户端(尤其配合流式传输时,可以边生成边返回)。
-
vLLM 提供了两种在线API:
- OpenAI-Compatible API Server(官方推荐):兼容OpenAI的Completions和Chat API协议,使用方式与OpenAI API相同。
- Simple Demo API Server(官方已不再维护):仅适用于简单测试,不推荐在生产环境使用。
-
示例命令
(OpenAI-Compatible API):
# 1) 启动服务 python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-2-7b-hf # 2) 用curl发送请求(OpenAI风格) curl http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/Llama-2-7b-hf", "prompt": "San Francisco is a", "max_tokens": 7, "temperature": 0 }'
-
背后原理:
- 异步化处理:vLLM使用uvicorn + fastapi 实现一个异步服务器,内部封装了
AsyncLLMEngine
。 - 排队 + 调度:当多个请求同时到来,vLLM会先将请求放入一个异步队列,然后在内部调度器(Scheduler)的管理下,将请求动态加入“running队列”进行推理。
- 先完成先返回:只要有请求在当前推理阶段完成,就可以立即把结果返回给客户端,不需要等待其他请求一起结束。若开启流式输出,还能在生成过程中分段发送token给客户端。
- 同样使用PagedAttention:在线服务依然会利用PagedAttention进行显存优化;当显存不足时,还会做部分数据的swap到CPU或暂时排队等待,以提升吞吐。
- 异步化处理:vLLM使用uvicorn + fastapi 实现一个异步服务器,内部封装了
二、LLMEngine:vLLM的内核引擎
无论是离线批处理还是在线服务,都依赖于同一个核心组件——LLMEngine。它的主要功能包括:
- add_request()
- 将每个请求转换成vLLM内部的统一数据结构(如SequenceGroup),并加入调度器(Scheduler)的waiting队列中。
- 离线批处理场景下,这个函数是“同步式”的:会依次处理batch中的各条数据。
- 在线场景下,会被重写为异步版本(
AsyncLLMEngine
),以便在协程中对每个请求进行处理。
- step()
- 执行一次推理阶段(prefill算1次推理,decode的每一步也算1次推理)。
- 调度器(Scheduler)决定哪些请求可以进入“running队列”,并为它们分配所需的显存块(Physical KV blocks)。
- 模型(带有PagedAttention)根据这些分配信息执行推理,生成新的token并更新KV缓存。
- 如果有请求在本次推理阶段完成(如达到或其它终止条件),则释放对应显存块,并将该请求从running队列中移除。
- abort_request()
- 如果在推理过程中客户端断开连接,或用户取消了请求,可以调用此方法中断推理,释放对应资源。
总结:只要理解了
add_request()
和step()
这两个函数的核心逻辑,就掌握了LLMEngine的大部分精髓。它们背后涉及到很多“排队、调度、显存分配、PagedAttention”的复杂逻辑,源码中会有大量的封装与嵌套。
三、离线批处理与在线服务的内核差异
- 离线批处理
- 对用户来说:一次性输入所有数据 -> 一次性得到全部输出。
- 对LLMEngine来说:内部依然是“动态批”调度,不断有请求完成并释放显存,再让新的请求进来,但对用户是不可见的,属于“内部黑箱”。
- 在线服务
- 对用户来说:随时可以发送请求;完成的结果可以第一时间返回。
- 对LLMEngine来说:依旧是同一个调度器;不同在于用户随时可能发来新请求或断开已有请求,因此需要用异步队列、异步I/O的方式处理。
- 核心思路相同:都是把请求排队 -> 等到一个推理阶段到来时,把合适数量的请求放入“running队列” -> PagedAttention完成这批的计算 -> 释放已完成请求的显存 -> 重复。
代码整体架构
这张图展示了 vLLM 内部代码整体架构,可以把它拆分为“中央控制端(Centralized Controller)”和“分布式Worker”两大部分。LLMEngine 就位于中央控制端的进程里,负责调度和管理请求,而真正的模型推理(包括PagedAttention等核心逻辑)会在一个或多个分布式Worker进程上执行(每个GPU对应一个Worker)。以下是对各模块的更详细解读:
一、LLMEngine 与 Centralized Controller
这部分都在同一个CPU进程中(如图中左侧所示),它包含了:
- LLMEngine(vllm/engine/llm_engine.py)
- 这是用户直接调用的接口层,用于接收请求(prompt)并将其转给调度器进行处理。
- LLMEngine本身并不直接执行推理,而是协调Scheduler、BlockSpaceManager等组件,并通过网络或进程通信,把模型计算任务发送给下游的Worker进程。
- Scheduler(vllm/core/scheduler.py)
- 调度器:决定每一个“推理阶段”(如prefill或decode的某一步)要处理哪些请求,并且以什么方式把这些请求打包交给Worker来执行。
- 当有多个请求或多个batch同时到来时,Scheduler会根据显存使用情况和优先级策略决定如何将它们安排到running队列、waiting队列或swap到CPU内存。
- 在运行完某个阶段后,如果有请求产生了新token或已经完成推理,就相应地更新这些请求的状态并做后续处理(例如释放显存块)。
- BlockSpaceManager(vllm/core/block_manager.py)
- 块空间管理器:负责管理所有token KV缓存的“块”在显存/内存中的分布情况,以及它们的生命周期。
- 它会与调度器配合,决定在每个推理阶段,需要为哪些请求分配新的缓存块或回收哪些已经不再使用的块。
- BlockAllocator(vllm/core/…)
- 显存/CPU分配器:将具体的物理内存(包括GPU显存、CPU内存)分配给BlockSpaceManager所需要的KV缓存块。
- 当GPU显存不足时,可能将部分块swap到CPU,以腾出空间给新的请求。
- 对应地,BlockSpaceManager会记录块在GPU上或CPU上,并在需要计算时重新把块拉回GPU。
总结:Centralized Controller进程中,LLMEngine是对外的“门面”,Scheduler是核心的调度大脑,BlockSpaceManager与BlockAllocator则负责管理KV缓存块在不同硬件介质间的具体分配和回收。
二、Distributed Workers(分布式Worker 进程)
如图中右侧部分所示,vLLM可以在分布式环境中使用,每个Worker通常对应一块GPU(也可在CPU模式或Ray模式下运行)。当前版本主要是单主机多GPU的场景,后续可能扩展为更多分布式方案。下列是Worker内部的关键组件:
- Worker(vllm/worker/worker.py)
- Worker是一个进程,里面包含了CacheEngine、Worker.model等对象。
- 当Centralized Controller决定某些请求要进行推理时,就会将这些请求对应的KV块和输入数据传给某个Worker,由它执行模型计算并返回结果(新token、注意力分数等)。
- CacheEngine(vllm/worker/cache_engine.py)
- 负责与PagedAttention配合,在Worker本地对KV缓存进行读写操作。
- 当收到Scheduler的指令后,CacheEngine会根据请求发送的数据ID或位置,把对应的KV块取出来(或者新建)并调用PagedAttention进行推理中的注意力计算。
- Worker.model(vllm/model_executor/models/…)
- 真正加载了大语言模型权重的模块。
- 当需要进行前向推理时,Worker调用它来计算每一步生成所需的logits、隐层状态等,内部再调用PagedAttention的相关逻辑做注意力机制。
- PagedAttention(vllm/model_executor/layers/…)
- 在Worker的模型层中具体实现的“分页/分块式注意力”机制。
- 它将传统的KV缓存细粒度地切分为多个Block,并在计算注意力时只加载、更新相关Block,避免重复计算和显存浪费。
- 与CacheEngine、BlockAllocator共同配合,实现动态地分配、回收和复用KV缓存。
总结:分布式Worker相当于是实际执行大模型推理的“工作站”,里面包含模型与缓存管理等功能,通过PagedAttention和CacheEngine实现对KV缓存的高效操作。每个Worker与中央控制(Scheduler)相互通信,将请求对应的工作内容取过来执行,然后返回结果。
三、集中式调度 + 分布式推理 的好处
- 统一调度,简化架构
- 由于Scheduler和BlockSpaceManager都在主进程上进行集中管理,减少了多GPU之间的“谁分配什么资源” 的冲突和协商开销。
- Worker只需专注于执行分配到的计算任务,无需关心整体系统状态。
- 灵活的显存管理
- Centralized Controller可以全局掌握所有请求与所有GPU/CPU的使用情况,做全局调度;比如当某块GPU显存满了,可以直接把部分KV块swap到CPU或调度到其他GPU。
- Worker仅保留自己当前需要用到的块,剩余部分由BlockAllocator/BlockSpaceManager在后台统一管理。
- 扩展为多Worker
- 如果单GPU算力不足,可增加多个Worker进程,每个进程对应一块GPU或一组GPU,分担推理工作。
- 在后续版本中,vLLM也有望支持更多分布式策略(如TP/PP并行),进一步提升推理吞吐。
四、代码阅读指南
- vllm/engine/llm_engine.py:
入口:LLMEngine
和AsyncLLMEngine
。用户调用接口层,和Scheduler/BlockSpaceManager配合完成推理。 - vllm/core/scheduler.py:
核心调度逻辑所在,负责推理阶段的决策,让哪些请求进入执行队列、哪些swap到CPU等。 - vllm/core/block_manager.py & vllm/core/block_allocator.py:
管理KV缓存块的创建、删除、swap和引用计数。 - vllm/worker/worker.py:
定义Worker进程的核心流程,包括接受任务、调用模型执行、返回结果。 - vllm/worker/cache_engine.py:
在Worker侧管理KV缓存块的读写与paged attention计算对接。 - vllm/model_executor/:
这里存放模型相关的执行逻辑与PagedAttention的实现。
加载模型与预分配显存
下面的内容主要围绕 vLLM 在正式启动推理服务前,会做的两件初始化工作:
- 加载模型(将大模型权重加载到 Worker 上)
- 预分配显存(在 GPU/CPU 上提前创建一批 KV Cache 块)
这样,当真正有请求(prompt)到达时,vLLM 就能直接使用这些预分配好的缓存块,而无需在推理过程中再做大规模内存申请或分配,极大提高了服务的启动效率和稳定性。下面会分步骤为你详细讲解其中原理和流程。
一、加载模型
1.1 加载的时机与方式
-
时机:在 vLLM 正式开始处理第一个请求之前,就会先把你指定的基础模型(base model)加载到 Worker 进程中。
-
方式:默认情况下,vLLM 使用 HuggingFace 模型仓库(Model Hub)来下载并加载对应的模型权重。
- 如果你在使用在线加载方式,只需在启动参数或环境变量中指定相应的模型名称(如
"facebook/opt-125m"
、"meta-llama/Llama-2-7b-hf"
等),vLLM 便会从 HuggingFace 上自动拉取权重并初始化。 - 也可以通过设置环境变量,把默认源改为其他仓库(如 ModelScope)。
- 如果你在使用在线加载方式,只需在启动参数或环境变量中指定相应的模型名称(如
1.2 多 Worker 环境下的加载
- 每个 Worker(对应一块 GPU 或者 CPU 进程)会各自加载同一份基础模型权重到本地显存或内存中。
- 当进入推理阶段时,Worker 就可以直接对这些已加载好的模型权重做前向计算(forward),结合 PagedAttention 完成生成任务。
提示:如果模型比较大,加载过程会消耗一定时间和显存。为保证服务稳定,通常你会先在所有 Worker 上完成加载,然后才开始对外提供推理 API。
二、预分配显存
在模型加载完成后,vLLM 还会进行一次“模拟实验”或“内存探测(profile)”,借此估算自己能创建多少 KV Cache 物理块,并为后续真正的推理分配足够的缓存空间。图示中称之为 “Before serving requests - 2. Profile memory usage” 和 “3. Pre-allocate KV blocks”。
2.1 为什么要预分配?
- 减少推理时的分配开销:如果把创建 KV Cache 的操作放到推理进行时才做,大规模的内存分配可能导致延迟抖动或显存碎片。
- 让调度器(Scheduler)更好工作:调度器需要知道 GPU/CPU 剩余多少块可以使用,才好动态决定将多少请求同时放到“running队列”里。
- 便于发现配置或显存不足问题:如果你给定的
max_num_seqs
、max_num_batched_tokens
太大,导致需要分配的 KV Cache 块数量远超 GPU 真实承受能力,vLLM 能在初始化时就发现并报错,而不是推理时才崩溃。
2.2 关键参数
初始化 LLMEngine 时,用户通常会提供两个重要的配置项:
- max_num_seqs
- 在一个推理阶段里,vLLM Engine 最多能处理的序列(seq)数量。
- 默认为 256,意味着同时处理 256 条请求(或 256 条不同的上下文)是上限。
- max_num_batched_tokens
- 在一个推理阶段里,vLLM Engine 最多能处理的 token 数量。
- 默认为 2048,表示同时会处理的 token 数总量上限。
vLLM 会据此来估计:如果在一个推理阶段,有多条请求,每条请求的序列长度合计多少 token,需要大概多少块 KV Cache,GPU/CPU 能否容纳得下。
2.3 计算分配给 KV Cache 的总显存
在做内存 profiling 的过程中,vLLM 会先用一批“虚拟请求”来做一次假想的前向推理(prefill 或 decode),测量不使用 KV Cache时模型本身的显存占用是多少(包括模型权重 + 中间激活等)。然后再去算:
分配给KV cache显存 = gpu总显存 - 不使用KV cache做1次推理时的显存占用(包括模型本身和推理过程中的中间数据)
这一步相当于先排除模型必需的显存占用后,看看还剩多少“空闲”容量可以用来分配 KV 缓存。
2.4 计算每个 KV Cache 块的大小
- block_size:
每个物理块包含多少个 token 的 KV 内容(vLLM 默认是 16 个 token)。 - 其它参数:
包括 Transformer 层数、head 数、head_size 以及数据类型(fp16/fp32)等等。 - 最终会得到类似这样一个公式:
KaTeX parse error: Expected 'EOF', got '_' at position 13: \text{cache_̲block_size} = \…
其中“ × 2 \times 2 ×2”是因为在多数实现里 KV 是分开存储的(Key 和 Value 都需要分配)。如果是 fp16(dtype_size=2 bytes),则还要代入这个数。
2.5 计算可分配的总块数
一旦知道可分配给 KV cache 的总显存和单块大小,就能算得总块数:
KaTeX parse error: Expected 'EOF', got '_' at position 11: \text{num_̲blocks} = \frac…
对于 CPU 端同理,只不过 CPU 内存通常比较大(默认上限可能写成 4GB、8GB 或用户自定义),可以用类似的逻辑算出可分配的块数(当 GPU 空间不够时,Scheduler 会把一部分块 swap 到 CPU)。
2.6 在 GPU 上创建预分配的空 Tensor
最后,vLLM 会调用类似 BlockAllocator._allocate_kv_cache()
这样的函数,在 GPU 或 CPU 上逐层(num_layers 次)创建这些空张量(torch.empty(...)
),并将它们存放到一个列表里,组成 “KV Cache” 池。
- 这些空张量就相当于分割好的“片段”,每个分片能容纳一部分 token 的键值对。
- 当有新请求到来,需要一个 block 时,BlockManager/BlockAllocator 只要把一个空闲块分配给它,无需再进行张量创建。
优点:
- 减少了运行时的 Fragmentation(碎片化)风险。
- 可以快速映射到调度策略,让 Scheduler 知道当前空闲块、在用块、swap 块的数量与位置,进行更精细的管理。
三、预分配带来的好处与注意点
- 启动后显存占用“突增”是正常现象
- 由于 vLLM 把一部分空张量提前开辟好了,你可能会看到 GPU 显存一下子就用掉了相当大比例。但这些张量实际是空数据,只是占位;真正放入 Token KV 时才会写入内容。
- 参数调得过大可能导致 OOM
- 如果你指定的
max_num_seqs
和max_num_batched_tokens
太高,而 GPU 显存不足,vLLM 在初始化时就会报错或内存不足的问题,而不是推理时才发现。 - 这其实是个安全机制,让你知道当前配置不可行。
- 如果你指定的
- 调度器与预分配
- 一旦 KV block 预分配完毕,Scheduler 就能准确地知道有多少块可用,并且在推理阶段分配/回收这些块给不同请求。如果用完了,新的请求就只能等,或者把不活跃的块swap到 CPU。
- 可能影响多进程部署
- 在多进程(或多容器)中部署时,各自进程都要预分配一份 KV cache,会进一步增加显存占用,需要格外留意调优。
四、小结
- 加载模型:先把大模型的权重加载到每个 Worker 上,保证 Worker 拥有完整的计算能力。
- 预分配显存:
- 内存测量:通过一次模拟推理,估计模型本身占用多少显存,并计算剩余可用于 KV cache 的空间;
- 计算块大小 & 总块数:基于 block_size、头数、层数等模型结构,推断需要多少块;
- 在 GPU/CPU 上创建空 Tensor:一下子把这批块都预先分配好,供后续推理阶段快速使用。
这两步完成后,vLLM 才算真正“就绪”,能够在请求到来时立即投入推理,无需临时分配更多大块内存,从而减少延迟、提高吞吐与稳定性。
从这部分内容也能看出,vLLM 在设计上十分注重实际系统工程需求:
- 启动时就对显存做充分的准备;
- 运行时则配合调度器和 PagedAttention,以“块”为单位执行分配与回收,实现弹性、灵活的高并发推理。
Scheduler调度
下面这部分内容讲的是 vLLM 在推理阶段如何利用调度器(Scheduler)对请求进行分配、执行、以及在显存不足时如何处理“swap”等操作。简单来说,调度器需要在每一个推理步骤(Prefill 或 Decode 的某一时刻)动态地决定哪些请求进入运行、哪些需要等待、哪些请求要被暂时Swap到CPU内存。下面会分几个要点为你详细拆解。
1. 三种队列:Waiting / Running / Swapped
1)Waiting队列
- 刚刚到达、尚未开始推理或者中途需要继续等待显存的请求,会被放在等待队列里。
- 当调度器检测到有空闲的KV缓存块或GPU显存足够时,就可以把这些请求从Waiting队列拉到Running队列里执行。
2)Running队列
- 当前正在GPU上执行推理(prefill或decode)的请求。
- 这些请求已经被分配了相应的KV缓存块(Block),可以进行一步或多步的前向计算。
- 如果请求完成生成,或者中途需要释放资源,调度器会将它从Running队列移出。
3)Swapped队列
- 当GPU显存紧张、KV缓存块无法满足新的token生成时,调度器可能会把部分正在运行的请求(尤其是“后到达”或“优先级较低”的请求)从GPU“换出”(swap)到CPU内存里,以释放GPU空间给其他请求继续运行。
- 被swap出去的请求,其KV缓存也会被移到CPU上保存。等待下一次调度时,如果GPU显存再度可用,这些请求可重新加载回到GPU并恢复推理。
提示:Swapped队列在前面章节的示例图里未出现,这里才正式提出,表示vLLM在高并发、显存不足的情况下会使用“swap”策略。
2. 预备知识:后来先抢占 (Preemption)
在 vLLM 的调度策略里,有一项关键手段叫**“后来先抢占”**(Preemption),大致含义是:
如果一个新的请求到来(或正在等待中),需要分配KV缓存,但 GPU 上已没有空闲块可用,调度器会从 Running队列里“挑出最晚加入的请求”(或优先级更低的请求)将其 Swap 到 CPU,把腾出来的块让给新的(或优先级更高的)请求继续推理**。
这是典型的“后进先出”式抢占策略,目的是:
- 尽快满足新请求或更高优先级请求的需求(尤其是在在线服务模式下,对新的交互请求响应速度至关重要)。
- 被swap出去的请求也并非放弃,只是暂时中断;等到GPU显存重新腾出块后,再将它们从Swapped队列移回Running。
3. 调度器在每一步做什么?
正如图示“At every step, the scheduler”所示,每一个推理步骤(包括一次prefill或一次decode)开始时,调度器要做的事情主要有:
- 决定本次要运行的请求集合
- 如果GPU上还有空闲的KV缓存块,调度器就从Waiting队列里取请求加到Running队列;
- 如果没有可用块,且有新请求需要空间,则可能触发Swap,把一些正在Running的请求移到Swapped,以腾出块给新请求。
- 调用BlockSpaceManager和BlockAllocator
- 为新加入Running队列的请求分配实际的KV块;
- 处理块共享(多个请求可能共享某些前缀块);
- 回收或删除已经完成的KV块;
- 对被Swap或被抢占的请求,其KV块会移动到CPU或被标记为“swapped out”。
- 更新请求的状态
- 已完成生成(到达或最大长度)的请求,从Running队列移除,释放块。
- 中途被Swap到CPU的请求,改到Swapped队列;
- 其它仍在进行的请求,继续保留在Running队列,以便下一步decode阶段接着计算。
4. 处理流程示例
可以用一个简单的示例来说明:
- 有新的请求A到达
- 如果GPU上还有多余块,调度器把请求A从Waiting -> Running,并在BlockAllocator中为它分配若干物理块。
- 这时又来了请求B,而GPU已无可用块
- 调度器查看Running队列,找到最晚加入或优先级低的请求X,将它从Running -> Swapped,并把对应KV缓存块swap到CPU内存。
- 释放的块就能分配给请求B。
- 请求X暂时在Swapped队列等待,下次调度看有没有机会回到GPU。
- 下一次解码阶段时
- 如果GPU上又有空闲块(比如某个请求Y已经完成生成,释放了块),调度器就可以从Swapped队列拉回请求X(从Swapped -> Running),把它的KV缓存重新搬回GPU上,让它继续未完成的解码。
- 重复这一过程,直到所有请求都完成或被abort。
5. 优势与注意点
- 充分利用显存
- 当有空闲块就多接收新的请求,加速吞吐;
- 当显存吃紧时,就对暂时不急或后到的请求进行Swap,保障系统不会崩溃或OOM。
- 保证性能与响应
- “后来先抢占”优先给新请求或更高优先级请求留出显存,可以减少高优先级请求的等待。
- 但也意味着被Swap出去的请求要等一段时间才能回来继续解码,性能上会受点影响。
- Swap带来的I/O开销
- 每次Swap都需要将KV缓存数据从GPU转移到CPU,对带宽和速度有一定损耗。
- 这在高并发场景下是必要的折衷:总比让请求一直等待或者触发OOM要好。
- 队列状态的监控
- 在实际部署时,可以观察到Scheduler日志里waiting/running/swapped三类队列的规模变化;
- 如果Swapped队列长期过大,可能需要减小并行度或增加GPU资源。
6. 小结
通过这部分内容,你可以看到在 vLLM 里,“调度器”(Scheduler)发挥了至关重要的角色——在每个推理阶段,决定哪些请求可以用GPU继续生成,哪些请求要放在waiting或swap。这一切都围绕着“KV Cache块”这种核心资源展开:
- 有空闲块 → 请求能进“running”,
- 无空闲块 → 可能触发“swap”以释放块,
- 完成或终止 → 回收块并移出队列。
同时,“后来先抢占(Preemption)”进一步让vLLM在GPU显存紧张时能优雅地处理新请求并避免系统崩溃,从而使得在线推理服务在高并发、大模型、有限显存的情况下依旧能维持可观的吞吐与响应速度。
总而言之,这就是图中展示的调度流程:当请求到来 -> LLMEngine将其加到Scheduler等待 -> 每步推理(prefill/decode)之前,Scheduler根据可用块分配或swap -> Worker执行运算 -> 重复直到请求结束。这也是 vLLM 能在实践中高效运行的关键所在。