vLLM 基于 uvicorn + FastAPI 的异步 Web 框架构成。vLLM 的主体是 LLMEngine,它是一个单例类,负责管理所有的模型和数据。在异步 API 中使用的是一个 AsyncEngine。在分析 AsyncEngine 之前,我们先将 Web 部分单独拆出来看一下。
vLLM 的 CLI 入口是 vllm/scripts.py,其中 serve 的启动是通过 uvloop.run 的方式启动的。uvloop 是一个替代默认 asyncio 事件循环的库,它使用 libuv 作为事件循环的实现,从而提高性能。uvicorn 是一个基于 uvloop 的 ASGI 服务器,它可以将 ASGI 应用部署到 Web 服务器上。FastAPI 是一个基于 Starlette 的 Web 框架,它提供了许多便利的功能,比如自动文档生成、请求参数校验等。
参数经过解析以后会进入 run_server,通过 uvloop.run(run_server(args))。run_server 在 entrypoints/openai/api_server.py 下面。AsyncEngineArgs.from_cli_args(args) 使用命令行参数初始化 AsyncEngineArgs,如果要自行封装的话可以直接初始化 AsyncEngineArgs。AsyncEngineArgs 继承自 EngineArgs,其中的参数都是用来控制推断命令的。
比较常用的几个参数:
-
model: 模型的路径,可以是一个目录,也可以是 hf 上的一个 repo。
-
model_name: 如果是目录的话,期望的模型名称,或者想要改个别名,对应的是 API 中指定模型的名称。
-
tensor_parallel_size: tensor parallel 副本数,如果用多个 GPU 可以用到,会根据这个将 kv head 平分到不同的 GPU 上。
-
pipeline_parallel_size: pipeline stages 数,如果用多个 GPU 可以用到,会根据这个将模型的前向计算的layers分成多个阶段,每个阶段在不同的 GPU 上计算。
可以参考下面这个例子:
假设我们有 8 个 GPU,分别表示为 g0 … g7,并且我们使用 2 个 GPU 来并行化模型张量,使用 4 个 GPU 来并行化模型流水线。当前函数将创建 4 个张量模型并行组和 2 个流水线模型并行组:
4 个张量模型并行组:
- [g0, g1]
- [g2, g3]
- [g4, g5]
- [g6, g7]
2 个流水线模型并行组:
- [g0, g2, g4, g6]
- [g1, g3, g5, g7]
注意,为了提高效率,调用者应确保相邻的 rank 位于同一个 DGX 盒子上。例如,如果我们使用 2 个 DGX-1 盒子,总共有 16 个 GPU,rank 0 到 7 属于第一个盒子,rank 8 到 15 属于第二个盒子。
-
num_seqs: 最大的序列数,其实就是 batch size,会翻倍得增加显存使用,这个貌似在启动之前的 profile 阶段可能会导致大量显存的占用。
-
quantization: 量化的方法,可以是 bitsandbytes 等,可能需要和 load_format 结合使用。
-
load_format: 加载模型的格式,可以是 pt, safetensors, bitsandbytes 等等,如果用到量化的模型基本要改成 bitsandbytes。
-
dtype: 数据类型,fp32, fp16,bf16 等等,如果模型是 bf16 的话,他默认是 bf16 的模型用 bf16,有些显卡不支持 bf 浮点数所以要设置成 half 也就是 fp16。
-
host: 监听地址。
-
port: 监听端口。
-
max_model_len: 上下文长度,适合显存不足的显卡,把默认的上下文长度改下一点。
-
enforce_eager: 是否强制使用 eager 模式,如果显存不够的需要开启这个模式,不完全加载计算图的方式可以减少显存的使用。
api_server 中的 build_app 会使用 APIRouter 初始化路由,并通过 app.include_router 引入。
主要看 @router.post("/v1/chat/completions") 注册的 async def create_chat_completion 是最常用的函数调用。
init_app_state 在 app.state 中保存了 openai_serving_chat,以及其他一些接口的状态,这取决于模型配置中是否包含这些功能。例如,文本嵌入等功能(通常都有)。当调用 create_chat_completion 时,会调用 openai_serving_chat 对应的 OpenAIServingChat 类的方法。因此,Serving 的主体可以通过查看这个对象的方法来理解其功能。
构建 AsyncEngine -> 构建 app 对象。