文章目录
- 介绍
- vLLM 速度很快
- vLLM 灵活且易于使用
- 使用和部署
- 离线批量推理
- 在线推理服务
- 分布式推理服务
- 如何确定分布式推理策略?
- 多节点推理和服务-Ray集群
- 量化支持
- 模型量化
- KV Cache 量化
- 性能调优
- PagedAttention
- Automatic prefix caching
- Batching 策略
- Preemption
- Chunked Prefill
- 性能分析
- 分析步骤
- 分析案例
- Q&A
介绍
vLLM 是一个快速且易于使用的 LLM 推理和服务库。
vLLM 速度很快
- 一流的服务吞吐量
- 使用 PagedAttention 高效管理注意力键和值内存
- 持续批处理传入请求
- 使用 CUDA/HIP 图快速执行模型
- 量化:GPTQ、AWQ、INT4、INT8 和 FP8
- 优化的 CUDA 内核,包括与 FlashAttention 和 FlashInfer 的集成。
- 推测解码
- 分块预填充
vLLM 灵活且易于使用
- 与流行的 HuggingFace 模型无缝集成
- 采用多种解码算法实现高吞吐量服务,包括并行采样、波束搜索等
- 分布式推理的张量并行和流水线并行支持
- 流式输出
- 兼容 OpenAI 的 API 服务器
- 支持多种硬件平台,包括 NVIDIA GPU、AMD CPU 和 GPU、Intel CPU 和 GPU、PowerPC CPU、TPU 和 AWS Neuron 等
- 前缀缓存支持
- 多 LoRa 支持
使用和部署
离线批量推理
from vllm import LLM, SamplingParams# Sample prompts.
prompts = ["Hello, my name is","The president of the United States is","The capital of France is","The future of AI is",
]
# Create a sampling params object.
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)# Create an LLM.
llm = LLM(model="facebook/opt-125m")
# Generate texts from the prompts. The output is a list of RequestOutput objects
# that contain the prompt, generated text, and other information.
outputs = llm.generate(prompts, sampling_params)
# Print the outputs.
for output in outputs:prompt = output.promptgenerated_text = output.outputs[0].textprint(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
以上是一段本地离线的推理代码,其中模型参数可以直接替换 HF 的模型,启动脚本的时候会自动下载模型到本地,然后编译启动引擎。要注意本地跑的时候需要查看你的机器显存是否支持模型所需显存大小。
在线推理服务
- 下载镜像
vLLM 提供了一个官方的 Docker 镜像用于部署,这个镜像可以用来运行与 OpenAI 兼容的服务,并且在 Docker Hub 上可用,名为 vllm/vllm-openai。
# https://hub.docker.com/r/vllm/vllm-openai
docker pull vllm/vllm-openai# 国内测试机无法直接下载(试了很多国内加速器都不能正常下载),可以在海外机器下载镜像,然后上传到国内自有库,再从国内自有库下载镜像# 国内自有库下载镜像
docker pull swr.cn-east-3.myhuaweicloud.com/kubesre/docker.io/vllm/vllm-openai:latest
- 下载模型
# 海外机器
huggingface-cli login --token **** # 在登录hf后,token在右上角settings-token里面
huggingface-cli download --resume-download meta-llama/Llama-3.1-70B-Instruct --local-dir /data/meta-llama/Llama-3.1-70B-Instruct # 国内测试机器
wget https://hf-mirror.com/hfd/hfd.sh
chmod a+x hfd.sh
export HF_ENDPOINT=https://hf-mirror.com
apt-get update
apt-get install -y aria2
aria2c --version
apt-get install git-lfs
./hfd.sh meta-llama/Llama-3.2-11B-Vision-Instruct --hf_username Dong-Hua --hf_token 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 aria2c -x 4./hfd.sh homebrewltd/llama3-s-v0.1 --hf_username bifeng --hf_token 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 aria2c -x 4# 断点续传
aria2c --header='Authorization: Bearer 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' --console-log-level=error --file-allocation=none -x 4 -s 4 -k 1M -c 'https://hf-mirror.com/homebrewltd/llama3-s-v0.1/resolve/main/model-00002-of-00004.safetensors' -d '.' -o 'model-00002-of-00004.safetensors'
- 启动容器
引擎启动参数:https://docs.vllm.ai/en/stable/models/engine_args.html
docker run --runtime nvidia --gpus all \-v /data1/data_vllm:/data \-p 8001:8000 \--name qwen_llm_3 \--ipc=host \swr.cn-east-3.myhuaweicloud.com/kubesre/docker.io/vllm/vllm-openai \--model /data/Qwen2.5-72B-Instruct-GPTQ-Int4 \--max-model-len 102400# docker inspect qwen_llm_3
# python3 -m vllm.entrypoints.openai.api_server --model /data/Qwen2.5-72B-Instruct-GPTQ-Int4
- 接口文档地址:http://101.126.39.254:8001/docs,请求:
curl http://101.126.39.254:8001/v1/chat/completions \-H "Content-Type: application/json" \-d '{"model": "/data/Qwen2.5-72B-Instruct-GPTQ-Int4","messages": [{"role": "system", "content": "你是一个喜剧人"},{"role": "user", "content": "给我讲个短笑话"}],"max_tokens": 1024,"stop": "<|eot_id|>","temperature": 0.7,"top_p": 1,"top_k": -1}'
分布式推理服务
如何确定分布式推理策略?
- 单 GPU(no distributed inference):不存在分布式推理,只要模型运行的显存需求小于GPU显存大小即可部署服务。
- 单节点多 GPU(tensor parallel inference):如果模型太大,无法放入单个 GPU,但可以放入具有多个 GPU 的单个节点,则可以使用张量并行。张量并行大小是要使用的 GPU 数量。例如,如果在单个节点中有 4 个 GPU,则可以将张量并行大小设置为 4。
$ vllm serve facebook/opt-13b \ $ --tensor-parallel-size 4
- 多节点多 GPU(tensor parallel plus pipeline parallel inference):如果模型太大,无法放入单个节点,则可以将张量并行与流水线并行结合使用。张量并行大小在每个节点中使用的 GPU 数量,流水线并行大小是使用的节点数量。例如,如果在 2 个节点中有 16 个 GPU(每个节点 8 个 GPU),则可以将张量并行大小设置为 8,将流水线并行大小设置为 2。
$ vllm serve gpt2 \ $ --tensor-parallel-size 8 \ $ --pipeline-parallel-size 2疑问:不能使用单节点 16 个 GPU 吗? 参考:https://blog.vllm.ai/2024/07/23/llama31.html
多节点推理和服务-Ray集群
确保所有节点上的执行环境相同,包括模型路径、Python 环境。推荐使用 Docker 镜像确保相同环境,并通过将它们映射到相同的 Docker 配置来隐藏宿主机的异构性。
-
选择一个节点作为头节点,并运行以下命令:
$ bash run_cluster.sh \ $ vllm/vllm-openai \ $ ip_of_head_node \ $ --head \ $ /path/to/the/huggingface/home/in/this/node
-
在其余工作节点上,运行以下命令:
$ bash run_cluster.sh \ $ vllm/vllm-openai \ $ ip_of_head_node \ $ --worker \ $ /path/to/the/huggingface/home/in/this/node 注意: - ip_of_head_node 参数应该是所有工作节点都可以访问的头节点的IP地址 - 在任何节点上进入容器,执行 ray status 检查 Ray 集群的状态,都应该能看到正确数量的节点和 GPU
-
经过上面两步你得到了一个由容器组成的 Ray 集群。然后,在任何节点上你都可以像所有 GPU 都在一个节点上一样正常使用 vLLM。
- 通常的做法是将张量并行大小设置为每个节点的 GPU 数量,将流水线并行大小设置为节点数量。例如,如果在 2 个节点上有 16 个 GPU(每个节点 8 个 GPU),可以将张量并行大小设置为 8,将流水线并行大小设置为 2:
$ vllm serve /path/to/the/model/in/the/container \ $ --tensor-parallel-size 8 \ $ --pipeline-parallel-size 2
- 你也可以在没有流水线并行的情况下使用张量并行,只需将张量并行大小设置为集群中的 GPU 数量。例如,如果在 2 个节点上有 16 个GPU(每个节点 8 个GPU),可以将张量并行大小设置为 16:
$ vllm serve /path/to/the/model/in/the/container \ $ --tensor-parallel-size 16
为了使张量并行性能良好,应确保节点之间的通信是高效的,例如使用 Infiniband 等高速网卡,要正确设置集群以使用 Infiniband,需要在run_cluster.sh脚本中添加额外参数,如–privileged -e NCCL_IB_HCA=mlx5。确认 Infiniband 是否工作的一种方法是运行带有NCCL_DEBUG=TRACE环境变量的vLLM,例如NCCL_DEBUG=TRACE vllm serve …,并检查日志以获取 NCCL 版本和使用的网络。如果在日志中找到了[send] via NET/Socket,则意味着 NCCL 使用原始 TCP 网络通信,这对跨节点张量并行不高效;如果在日志中找到[send] via NET/IB/GDRDMA,则意味着 NCCL 使用带有 GPU-Direct RDMA 的Infiniband,这是高效的。
量化支持
模型量化
AutoAWQ:适合低延迟,不适合高吞吐(比未量化版本更低)
BitsAndBytes:减少内存使用并提高性能,而不会显著牺牲准确性
INT8 W8A8:vLLM 支持将权重和激活量化到 INT8,以节省内存和加速推理。这种量化方法特别适用于在保持良好性能的同时减小模型大小
FP8 W8A8:FP8 模型的量化可以将模型显存需求减少 2 倍,并在准确性影响最小的情况下提高吞吐量最多 1.6 倍
参数:
[–quantization {aqlm,awq,deepspeedfp,tpu_int8,fp8,fbgemm_fp8,modelopt,marlin,gguf,gptq_marlin_24,gptq_marlin,awq_marlin,gptq,compressed-tensors,bitsandbytes,qqq,experts_int8,neuron_quant,None}]
疑问:使用非量化模型,把启动引擎的量化参数设置为某一个,引擎启动的时候会对此非量化模型先进行量化处理吗,比如以下两种启动效果是一致的?
vllm serve meta-llama/Meta-Llama-3.1-405B-Instruct-FP8
vllm serve meta-llama/Meta-Llama-3.1-405B-Instruct --quantization fp8
答:在启动服务的时候后者会更慢,启动后效果几乎一致
KV Cache 量化
FP8 E5M2/E4M3 KV Cache:将 KV Cache 量化为 FP8 可以减少内存占用,增加了可以存储在缓存中的令牌数量,从而提高了吞吐量。
参数:
[–kv-cache-dtype {auto,fp8,fp8_e5m2,fp8_e4m3}]
性能调优
PagedAttention
参考文档:https://blog.vllm.ai/2023/06/20/vllm.html
- 内存优化
- 内存共享
Automatic prefix caching
参考文档同上
自动前缀缓存(Automatic Prefix Caching,简称 APC)会缓存已存在查询的 KV Cache,这样如果新查询与某个已存在查询共享相同的前缀,则可以直接重用 KV 缓存,从而让新查询跳过共享部分的计算。
Batching 策略
- Static Batching:传统的批处理方法,批处理大小是固定的,所有请求必须在批处理开始之前到达。
- Dynamic Batching:动态批处理允许在运行时动态地将请求组合成批次,相比 Static Batching 增加了时间的限制。
- Continuous Batching:连续批处理是一种策略,系统在处理过程中不断地将新到达的请求添加到当前批次中,直到满足特定的条件(如达到最大批次大小或超时)
- Inflight Batching:TensorRT-LLM 中 Continous Batching 一种叫法。
Preemption
由于 transformer 架构的自回归特性,有时 KV 缓存空间不足以处理所有批量请求。vLLM 可以抢占请求以释放 KV 缓存空间供其他请求使用,被抢占的请求在足够的 KV 缓存空间再次变得可用时会重新计算。
虽然这种机制确保了系统的鲁棒性,但抢占和重新计算可能会对端到端的延迟产生不利影响,如果经常遇到 vLLM 引擎的抢占,请考虑以下操作:
- 增加 gpu_memory_utilization,vLLM 通过使用 gpu_memory_utilization% 的内存来预先分配 GPU 缓存,通过增加此利用率可以提供更多的 KV 缓存空间。
- 减少 max_num_seqs 或 max_num_batched_tokens,这可以减少批量中的并发请求数量,从而需要更少的 KV 缓存空间。
- 增加 tensor_parallel_size(加 GPU),这种方法会打散模型权重,因此每个 GPU 有更多的内存可用于 KV 缓存。
Chunked Prefill
参考:https://zhuanlan.zhihu.com/p/710165390
vLLM 支持实验性功能 Chunked Prefill,Chunked Prefill 允许将大的 Prefill 分解成更小的块,并与解码请求一起批量处理,可以通过在命令行中指定 --enable-chunked-prefill
或在 LLM 构造函数中设置 enable_chunked_prefill=True
来启用该功能。默认情况下不开启 Chunked Prefill,vLLM 调度器优先处理预填充,如果模型最大上下文长度超过 32K 就会自动开启 Chunked prefill。
- 模型最大上下文长度超过 32K 就会自动开启其实好理解,请求上下文长度越长 prefill 阶段耗时越长,会直接让同一批的请求都等待 prefill 完成,才能开始 decode
- 如果我们手动开启 Chunked Prefill(开启之后优先 decode,批量请求达到最大 max_num_batched_tokens 之后直接送去解码,截断还未 prefill 完成的序列),max_num_batched_tokens 的调整对性能优化是有意义的,max_num_batched_tokens 设置越小延迟会越低,但吞吐量会降低;相反 max_num_batched_tokens 越大吞吐量越高,但延迟也越高。
这两个调优参数在最后的测试分析案例有做测试,可以看看案例的结论
性能分析
分析步骤
-
通过设置环境变量
VLLM_TORCH_PROFILER_DIR
到你想要保存追踪文件的目录来启用追踪,例如:VLLM_TORCH_PROFILER_DIR=/mnt/traces/
。OpenAI 服务也需要在设置了 VLLM_TORCH_PROFILER_DIR 环境变量的情况下启动docker run --runtime nvidia --gpus all \-v /data1/data_vllm:/data \-p 8001:8000 \-e VLLM_TORCH_PROFILER_DIR=/data/mnt/traces/ \--name qwen_llm_3 \--ipc=host \swr.cn-east-3.myhuaweicloud.com/kubesre/docker.io/vllm/vllm-openai \--model /data/Qwen2.5-72B-Instruct-GPTQ-Int4 \--max-model-len 102400
-
使用
benchmarks/benchmark_serving.py
脚本进行性能测试,输出测试数据集请求情况,以及内存跟踪日志:/usr/bin/python3 vllm/benchmarks/benchmark_serving.py --backend vllm --model /data/Qwen2.5-72B-Instruct-GPTQ-Int4 --dataset ShareGPT_V3_unfiltered_cleaned_split.json --profile --dataset ShareGPT_V3_unfiltered_cleaned_split.json:这个参数指定了用于测试的数据集 --profile:启用性能分析。意味着在运行测试时会收集性能相关的数据,如 GPU 和内存使用情况以及处理请求所需的时间。
-
上传跟踪日志到 https://ui.perfetto.dev/ 可以进行内存占用可视化分析
分析案例
vLLM 推理引擎性能分析基准测试
Q&A
Q: 如何在单个端口上使用 OpenAI API 同时提供多个模型的服务?
A: 目前这还不支持,你可以同时运行多个服务器实例(每个实例提供不同的模型服务),并需要增加一层来根据需要将传入的请求路由到正确的服务器。
系列文章:
一、大模型推理框架选型调研
二、TensorRT-LLM & Triton Server 部署过程记录
三、vLLM 大模型推理引擎调研文档
四、vLLM 推理引擎性能分析基准测试
五、vLLM 部署大模型问题记录
六、Triton Inference Server 架构原理