系列文章目录
vLLM (1) - Qwen2推理&部署
vLLM (2) - 架构总览
文章目录
前言
上一篇通过Qwen2
的vllm
推理和部署,对vllm
有了简单直观的了解,但是我们尚未涉及到它的工作原理。接下来我们将以vllm
源码为基础,逐步解析。本篇主要是对vllm
架构的总览。
一、官方资料
repo:https://github.com/vllm-project/vllm
文档:https://docs.vllm.ai/en/latest/
论文:https://arxiv.org/abs/2309.06180
二、原理简述
原理部分已经有不少文章已经讲解了,比如这篇文章。此小节的目标是:解释一些重要概念,简述PagedAttention
或者vLLM
原理,为后续架构和代码的分析做准备,毕竟本系列的侧重点是源码解析。
KV Cache
:这是一种提升推理效率的技术;简单来说就是,每次foward
推理生成下一个token
时,都要用到之前token
对应的key
和value
,因而可以把它们都缓存起来,这样就能节省推理时间;Prefill & Decode
:LLM
推理分为Prefill
和Decode
两个阶段;Prefill
(预填充)是将prompt
的所有token
一起喂给模型,这部分由于不需要采样,认为是可以并行的;Decode
(解码)是不断采样生成新的token
,由于是逐个生成,因此该阶段效率较低;KV cache
性能问题:1)此前很多推理系统是为每一个request
申请一块连续内存,此时只能选择所有requests中可能输出的最大长度来预分配KV cache
,这显然造成了资源浪费,因为短句用不了这么多KV cache
;2)实际情况下可能requests
比较多,某一时刻某个request
解码尚未完成,但始终完全占据为它预分配的连续内存,其中未使用的部分不能为新的request
所用,占着茅坑不拉屎;3)存在内存碎片,非连续内存也没办法被利用起来;PagedAttention
解决性能问题:使用类似虚拟内存分页管理的思想,简单来说就是,将申请的内存按照指定的大小block_size
划分成很多个block
,这些block
可以动态的分配给有需要的request
,极大减少了资源浪费;Physical blocks & Logical blocks
:Physical blocks
相当于物理内存,Logical blocks
相当于虚拟内存,两者之间有映射关系,KV cache
在Physical blocks
不连续,但是在Logical blocks
上是连续的。
上图是vLLM
同时处理两个请求时的KV cache
处理,对于一个请求来说,我只管问Logical blocks
要内存块,Physical blocks
上怎么存储我不管,Physical blocks
和Logical blocks
之间的映射关系会处理这个问题。
三、架构图
上图来源于vLLM First SF Meetup Slides
(官方repo中),它较为直观的展示了vllm的整体架构,让我来简单介绍一下:
- 直接处理用户请求的类是
LLMEngine
,它需要完成两部分内容,分别是(多个)请求的调度,以及模型的执行; Scheduler
是LLMEngine
的重要组成部分,它就是根据设定的调度策略,完成对用户请求的调度,以便调高吞吐,更快的响应;Scheduler
调度主要用到了虚拟内存分页管理的思想,将显存/内存划分成了很多个block
,那么这些block
的管理就由BlockSpaceManager
完成;- 另一边,
Worker
在模型执行和KV cache
缓存上发挥着重要作用,其中ModelRunner
主要负责模型加载和执行等,而CacheEngine
就负责KV cache
的初始化和更新。
四、项目结构
为了让大家更加清晰的理解vllm
的架构,我还列出了其项目结构(项目结构能直观反映它的架构),并给出了必要的注释,如下面所示。由于文件比较多,这边只展开到一级子目录。
这其中好些部分是我们暂不涉及的,我在注释中标注了(暂不涉及)
字样,这能减轻我们很大的压力。重点关注对象包括core/
、engine/
、executor/
和worker/
。
vllm/
├── attention/ # 注意力
│ ├── backends/ # 注意力各种后端实现,比如flash attention
│ ├── ops/
│ ├── layer.py
│ ├── selector.py
│ └── __init__.py
├── core/ # 核心,vllm最关键的部分
│ ├── block/ # 块,为指定的序列管理物理块
│ ├── block_manager_v1.py # 块管理器v1,管理逻辑块和物理块之间的映射关系等
│ ├── block_manager_v2.py # 块管理器v2
│ ├── embedding_model_block_manager.py # 针对embedding模型的块管理器
│ ├── evictor_v1.py # 驱逐器v1,驱逐长时间未使用的物理块缓存,腾出空间
│ ├── evictor_v2.py # 驱逐器v2
│ ├── interfaces.py
│ ├── policy.py # 调度策略,比如fcfs(first come first serve)
│ ├── scheduler.py # 调度器,当多个请求到来时,需要调度以高效的方式完成推理,给到用户响应
│ └── __init__.py
├── distributed/ # 分布式设备相关内容(暂不涉及)
│ ├── device_communicators/
│ ├── communication_op.py
│ ├── parallel_state.py
│ ├── utils.py
│ └── __init__.py
├── engine/ # 推理引擎
│ ├── output_processor/ # 输出处理器,后处理
│ ├── arg_utils.py # 管理输入参数
│ ├── async_llm_engine.py # 异步llm_engine,用于部署,不支持batch推理
│ ├── llm_engine.py # llm_engine,线下推理,可以batch
│ ├── metrics.py # 指标,记录kv_cache的使用,延迟等
│ └── __init__.py
├── entrypoints/ # 部署server相关(暂不涉及)
│ ├── openai/
│ ├── api_server.py
│ ├── llm.py
│ └── __init__.py
├── executor/ # 执行器
│ ├── cpu_executor.py
│ ├── distributed_gpu_executor.py
│ ├── executor_base.py # 执行器基类
│ ├── gpu_executor.py # gpu执行器,比如我们使用的Nvidia单卡gpu
│ ├── multiproc_gpu_executor.py
│ ├── multiproc_worker_utils.py
│ ├── neuron_executor.py
│ ├── ray_gpu_executor.py
│ ├── ray_utils.py
│ ├── tpu_executor.py
│ └── __init__.py
├── logging/ # 日志
│ ├── formatter.py
│ └── __init__.py
├── lora/ # lora相关(暂不涉及)
│ ├── fully_sharded_layers.py
│ ├── layers.py
│ ├── lora.py
│ ├── models.py
│ ├── punica.py
│ ├── request.py
│ ├── utils.py
│ ├── worker_manager.py
│ └── __init__.py
├── model_executor/ # 模型执行器,主要是管理模型相关部分的
│ ├── guided_decoding.py
│ ├── layers.py
│ ├── models.py
│ ├── custom_op.py
│ ├── pooling_metadata.py
│ ├── sampling_metadata.py # 采样元数据
│ ├── utils.py
│ └── __init__.py
├── multimodal/ # 多模态部分(暂不涉及)
│ ├── base.py
│ ├── image.py
│ ├── registry.py
│ ├── utils.py
│ └── __init__.py
├── sepc_decode/ # 投机采样(暂不涉及)
│ ├── batch_expansion.py
│ ├── interfaces.py
│ ├── metrics.py
│ ├── multi_step_worker.py
│ ├── ngram_worker.py
│ ├── proposer_worker_base.py
│ ├── spec_decode_worker.py
│ ├── top1_proposer.py
│ ├── utils.py
│ └── __init__.py
├── transformers_utils/ # transformers相关的工具
│ ├── configs/
│ ├── tokenizers/
│ ├── tokenizer_group/
│ ├── config.py
│ ├── detokenizer.py
│ ├── image_processor.py
│ ├── tokenizer.py
│ └── __init__.py
├── usage/
│ ├── usage_lib.py
│ └── __init__.py
├── worker/ # worker,是executor的重要组成部分
│ ├── cache_engine.py
│ ├── cpu_model_runner.py
│ ├── cpu_worker.py
│ ├── embedding_model_runner.py
│ ├── model_runner.py # 负责加载和执行模型,准备输入张量等
│ ├── neuron_model_runner.py
│ ├── neuron_worker.py
│ ├── tpu_model_runner.py
│ ├── tpu_worker.py
│ ├── worker.py # worker,使用的是gpu
│ ├── worker_base.py # worker基类
│ └── __init__.py
├── block.py # 块(逻辑块,物理块)定义
├── config.py # 配置,输入参数按照功能区分构成多个配置
├── envs.py # 环境变量相关
├── inputs.py # 输入类定义
├── logger.py # 日志
├── outputs.py # 输出类定义
├── pooling_params.py
├── py.typed
├── sampling_params.py # 采样参数类定义
├── sequence.py # 序列Sequence和序列组SequenceGroup等的定义
├── utils.py
├── version.py # vllm版本
├── _C.abi3.so
├── _custom_ops.py
├── _moe_C.abi3.so
├── _punica_C.abi.so
└── __init__.py
总结
本篇通过原理简述、架构图和项目结构展示这几个部分对vllm
原理和架构做了解析,相信大家对vllm
有了更深入的认识,也为后续源码分析清楚了一些障碍。