vllm2 架构解析
vllm1中讲了paged attention相关,这是整个vllm推理的原理基础。接下来我们看下vllm的推理架构。
VLLM的核心组件:

可以看到有两部分构成,左边是Scheduler,右边是worker。Scheduler负责请求调度,从等待队列中选择接下来要处理的请求。Worker负责模型推理,使用模型对被调度的请求进行推理。
Scheduler
Scheduler使用iterative-level 策略对请求进行调度。被调度的请求在生成一个token后会被重新调度。得益于 itertive-level 策略,vLLM 能够在每一轮新的迭代时选择不固定数量的请求进行处理(即 batch size 每次都不一定相同),因此它能够尽可能多地处理请求。目前对 iterative-level 的实现有两种方式,一种是区分填充阶段和生成阶段,另一种是不区分这两个阶段。
这里有点绕,我举个例子,例子是不区分这两个阶段的,也就是提出iterative-level 策略的 Orca 系统实现过程:
假设系统接收到 5 个请求,这些请求的任务是生成不同长度的文本,且每个请求可能开始于不同时间点。
请求信息:
请求 A:需要生成 10 个 token。
请求 B:需要生成 15 个 token。
请求 C:需要生成 5 个 token。
请求 D:需要生成 8 个 token(中途加入)。
请求 E:需要生成 12 个 token(稍后加入)。
调度过程
第 1 轮迭代(prefill阶段):
系统调度请求 A、B、C(先到的请求)。
为 A、B、C 创建 KV cache 并生成第一个 token。
Batch size = 3。
第 2 轮迭代(生成阶段):
系统再次调度 A、B、C,生成各自的第二个 token。
此时请求 D 到达,系统将其加入调度队列,开始填充阶段,生成第一个 token。
Batch size = 4(包括 A、B、C 的生成阶段和 D 的填充阶段)。
第 3 轮迭代(混合阶段):
系统调度 A、B、C、D,生成其对应的下一个 token。
此时请求 E 到达,进入填充阶段。
Batch size = 5(包括生成阶段和填充阶段的混合请求)。
第 4~N 轮迭代:
每轮动态调整 batch size:
随着请求完成(例如 C 在第 6 轮完成),batch size 会减小。
如果新的请求到达,batch size 会增大。
每个请求单独生成 token,直到完成或达到限制。
但是!vllm的策略是区分的,Scheduler 中有 3 个队列,waiting(接受到的新请求会先放入 waiting 队列)、running(被调度的请求)和 swapped 队列(swapped 队列用于存放被抢占的请求,即当请求处于生成阶段时,但由于空间的不足,需暂时将 running 队列中优先级低的请求移到 swapped 队列)。在调度的时候scheduler会按照先到FIFO的原则从waiting队列中选择请求放入到running队列。此外,Scheduler 的另一个核心组件是 BlockSpaceManager,它主要负责块表的维护。
Worker
然后另一个模块是worker。Worker 负责模型的执行。如果模型过大,可以将模型切分到多个 Worker 共同完成请求的处理。假设模型有 4 层,现在有 4 张卡,可以设置 Tensor Parallel=4(注意:截止 v0.4.0,vLLM 还没有支持 Pipeline Parallel),则将模型切分为 4 份,每张卡存放模型的一部分。Worker 的一个核心组件是 CacheEngine,它负责 KV cache 的初始化以及 KV cache 的相关操作。
在初始化的时候主要初始化llmengine中的scheduler和work对象。scheduler初始化主要是block table的初始化,这里的block table是后续输入的prompt的内容,类似于kv cache block table。worker的初始化主要是型的初始化以及 KV cache 的初始化,如下图所示:

推理调度
假设vllm接到3个请求(记为 s0, s1, s2)并放入 waiting 队列中,它们的 prompt 分别为 “Hello, my name is”、“The future of AI is” 和 “The life is”。接下来开始 vLLM 的调度和处理。
第一轮处理:
假设 vLLM 在这一轮只能调度两个请求进行处理,那么根据先到先处理的原则,
会从 waiting 队列中选择 s0 (“Hello, my name is”) 和 s1 (“The future of AI is”) 放入到 running 队列。
对于 s0,Worker 生成的 token 为 Dustin,对于 s1,Worker 生成的 token 为 bright。同时,Worker 会将计算过程产生的 KV 值存储在 KV cache 中。如下图所示:

第二轮处理
由于 waiting 队列中还有一个请求 s2(The life is),因此,vLLM 在第二轮只会处理这一个请求,因为前面提到,vLLM 只会处理要么都是填充阶段的请求,要么都是生成阶段的请求。由下图所示:

第三轮处理
waiting 队列中没有要处理的新请求,所以会从 running 队列中选择此轮要处理的请求(这些请求均处于生成阶段)。但由于没有多余的空间,vLLM 只会选择 s0 和 s1 进行处理。
经过多轮调度和推理,最终完成 3 个请求的处理,以上就是 vLLM 的工作流。
更多推荐
所有评论(0)