解析 vLLM 架构及源码系列 - 整体架构

整体架构
最近准备梳理一下 vLLM 推理框架的源码结构,网上也有不少的文章,但是并没有找到一个心仪的技术架构图。
官方 Architecture
从官方的 Architecture 来看,vLLM 的架构是非常简单的,主要包括几个部分**:**

首先是入口层面:
在线推理: OpenAI-compatible API Server,一个兼容 openAI 标准的 API 服务
离线推理: 直接接收离线请求的 LLM 服务
然后是引擎层:
LLM Engine:核心的推理引擎
AsyncLLMEngine: 异步托管的推理引擎,本质上还是 LLM Engine
引擎的核心层包括四个重要的组件:
Input Processing: 处理输入请求,例如用 tokenizer 进行输入处理
Scheduling: 处理请求的调度,每一个 Step 执行哪些请求
ModelExecution: 执行请求
Output Processing: 处理输出,将大模型的输出转换为可读的语言
从技术架构来说,也算是描述清楚嘞 vLLM 核心的几个部分,从接入层到引擎层再到请求处理的核心组件,但是从工程的角度来说,这张图过于抽象了,丢失了很多细节。要知道 vLLM 是社区最成熟的推理框架,无论是功能、代码量还是使用的广泛程度,都是 Top 1 的。
很多新手,光从这个架构图很难理解到复杂的推理请求究竟是如何在 vLLM 内部流转的。
知乎版架构
知乎有一位大神猛猿,提供了一系列的 vLLM 源码剖析,以下是她提供的架构图。原文链接如下:图解大模型计算加速系列:vLLM源码解析1,整体架构。
这一系列的文章,对我理解 vLLM 的源码,提供了很大的帮助,进一步展开讲解了 LLMEngine、调度器逻辑、Worker 逻辑等几个核心的组件。
她绘制的架构图如下,其实与官方的架构图来说,基本的结构是类似的,不过作者加入了自己的理解,将总体架构分为了控制和执行,控制部分主要包含着调度、Block 块的分配、调度等,而执行层面主要是 Worker 部分的逻辑。
这种一分为二的剖析方式,对于新手是很友好的,我是搞云原生出身的,这种理解方式很接近于 Kubernetes 数据面和控制面的设计方式,当然 vLLM 并不是按照这种设计方式去设计的,但是并不妨碍我们按照这种架构去理解 vLLM 的源码结构。因此还是强烈推荐作者的这一系列源码解读的文章,也是目前能够看到的最好的源码解读系列。

工程版架构
所谓“六经注我,我注六经文”,借助猛猿的文章,我对于 vLLM 的理解进一步加深了,本着沉淀自己和进一步贡献自己微薄力量的想法,我也给出了自己对于 vLLM 的源码理解。
因为我过往对于技术架构的设计,更多从落地考虑,因此架构图的风格,偏向于工程化架构。
对于源码跟踪,我最关注的是数据流转的路径,就好像以前的 web 开发,我们关注的是一个 rest 请求,是如何在后端进行 MVC 架构中流转,最终对数据库的数据进行操作的。对于 vLLM 的工程实现,我同样对一个推理请求如何进入到 server,并如何输出了文本很感兴趣。
以下的架构图,不再对更顶层的技术架构进行赘述了,前面提到的两个架构图已经对顶层架构介绍的足够清晰了。这里针对一个在线请求的生命周期进行追踪,对 vLLM 的技术架构做如下的抽象和理解:
接口层:接口层负责对 http 协议的 API 请求进行处理,并返回给到客户端大模型的输出结果
引擎核心层:EngineCoreProc 进程就是引擎的核心层,所有的逻辑都由它托管,它通过两个关键的 ZMQ 队列与接口层进行交互
input_queue: 输入队列
output_queue: 输出队列
核心业务循环:run_busy_loop,所有的调度和执行相关逻辑都从这里驱动
调度器: 执行调度逻辑,从 process_input_queue 中获取请求放入 waiting 队列,并进行调度执行,执行中的请求放入 running 队列
执行器:接收调度器的结果,执行推理任务,并将 output 放入 output_queue 中
本架构是基于 vLLM 0.9.0 以上的代码进行分析,并聚焦在 V1 架构的调度逻辑中。

除了这几个关键组件以外,vLLM 还有一个重要的设计逻辑,那就是异步。从上面的架构图中就可以看出来,在接口层的处理就开始了异步操作,client 在发送 request 到接口层之后,接口层并不会一直同步处理 request,而是将 request 放到了队列中,然后直到从 output_queue 中收到了 response,才会返回 client 信息。
当然这个过程也不一定会很长,这仅仅只是同步和异步的处理逻辑有所区别而已。从 input_queue 到 process_input_queue 再到 batch_queue,异步处理逻辑贯穿了整个 vLLM 的架构,因此 ZMQ 是 vLLM 中的一个重要的通信组件,同样也意味着对 vLLM 进行源码调试也是极度困难的。
为什么 vLLM 要做大量的异步处理呢?想象一下宝贵的 GPU 时间在并发处理下的运作逻辑,如果并发请求是同步处理的,那么势必会导致大量的 GPU 等待。而 GPU 最擅长的就是大并发的数据处理,而 GPU 又造价昂贵,如何在有限的硬件资源中,去极大的提升性能,核心点就是谁能把 GPU 时间利用的更充分。
关于整体架构的部分,就介绍到这里,更多的关于通信、调度、执行、插件的逻辑,在后续的文章中会陆续展开。
版权声明: 本文为 InfoQ 作者【黄继承】的原创文章。
原文链接:【http://xie.infoq.cn/article/2e75c868705334ef394528505】。文章转载请联系作者。
评论