状态:草稿(2026-10-27),已完成池化与多模态实测,并行化未实测。 池化测量于 2026-10-27 使用单张 8GB 级笔记本 GPU(Ada 架构)、vLLM 0.31.0 进行。并行化与多模态未实测,仅记录了从官方文档(0.32.0)和论文中确认的内容。LoRA 多实例服务的实测请参考《Toss ML/AI Infra 准备》第 15 篇。 2026-10-27 新增。 获得批准后,我们下载了 vLLM 0.32.0 镜像以及 Qwen3-VL-2B-Instruct(Apache-2.0,safetensors 4.26GB),并补充了 2-1 节的多模态实测。这是初版中标记为“未实测”的内容。 编写规则:以实测数据为基础,无法测量的标注为“未实测”,未能确认的标注为“未确认”。
目录
- 1. 四种并行化:拆分什么
- 2. 多模态输入
- 2-1. 2026-10-27 实测:在 8GB 上部署 2B 视觉语言模型
- 3. 池化:嵌入与重排也使用相同的引擎
- 4. LoRA 多实例服务
- 5. 未能确认的内容
- 参考
1. 四种并行化:拆分什么
| 方式 | 选项 | 拆分内容 | 通信 | 适用场景 |
|---|---|---|---|---|
| 张量并行 (TP) | --tensor-parallel-size |
将单层矩阵切片分摊到各 GPU | 每层进行 all-reduce | 模型无法放入单张 GPU 时,在单台服务器内部 |
| 流水线并行 (PP) | --pipeline-parallel-size |
按前后层拆分,每张 GPU 承载若干层 | 仅在阶段间传输激活值 | 跨多台服务器、且没有 NVLink 时 |
| 数据并行 (DP) | --data-parallel-size |
不拆分,完整复制多份模型 | 几乎没有(MoE 除外) | 提升吞吐量时 |
| 专家并行 (EP) | --enable-expert-parallel |
将 MoE 的专家分摊到各 GPU | 将 Token 向专家侧进行 all-to-all | 大型 MoE 模型 |
- TP 的通信非常频繁。 vLLM 采用 Megatron-LM(Shoeybi 等人,2019)的方式切分矩阵,该方式在单个 Transformer 层的每次前向传播中会进行两次 all-reduce(注意力机制后和 MLP 后)。因此,当 GPU 互联像 NVLink 一样快时,收益最大。官方文档也建议在没有 NVLink 的 GPU(例如 L40S)上使用 PP 代替 TP。
- PP 的通信较少,但会产生气泡。 由于前一阶段完成后后一阶段才能工作,如果只看单个请求,GPU 们会交替闲置。通过持续输入多个请求可以填补气泡。文档建议在服务器内部使用 TP,服务器之间使用 PP(单机 GPU 数量 = TP,服务器数量 = PP)。
- DP 的 KV 缓存也是各自独立的。 由于每个引擎的前缀缓存是独立的,必须将相同的十分之一个前缀发送到相同的引擎才能命中缓存。根据 KV 选择引擎的路由是 Dynamo 篇的主题。文档中还提到,在 DP 中,
--max-num-seqs适用于各个引擎,但像--max-num-queued-reqs这样的统一队列上限则适用于整个服务器。 - MoE 采用混合使用的方式。 文档介绍了像 DeepSeek 这样的模型:注意力机制使用 DP,专家层使用 EP 进行拆分。
查看模型能否装下时,可以看启动日志中的两行: GPU KV cache size: N tokens 和 Maximum concurrency for M tokens per request: Kx 。本系列实验服务器的数据为“基于 40,208 个 Token、4,096 个 Token 请求时达到 9.82 倍”。文档指出,如果该并发度低于目标,则应增加 GPU。
由于只有一张 GPU,这四种方式均未实测。将两张同系列 GPU 通过 1GbE 互联并迁移 KV 的实测(分离服务的速度慢了 20 倍以上)记录在《Toss ML/AI Infra 准备》第 24 篇中,因此低速连接下连接 GPU 所付出的代价可以用该篇文章来代替说明。
2. 多模态输入
vLLM 使用相同的 OpenAI 兼容 API 来服务接收图像、视频和音频的模型。如果在聊天消息的 content 中放入 image_url, video_url, input_audio 片段,API 服务器的处理程序会下载并预处理媒体,然后发送给模型的编码器。一张图像在提示词中会被替换为多个占位符 Token,前缀缓存会将图像的哈希值混入该占位符块的哈希中,使得相同的图像之间可以共享缓存(参考第 2 篇的前缀缓存设计文档)。
在实际运维中,比起性能,更应该优先关注这一点。
- SSRF:由于服务器会代为下载请求给出的 URL,因此请求者可以探测到服务器能够访问的内部地址。文档建议使用
--allowed-media-domains来指定允许接收的域名,并使用VLLM_MEDIA_URL_ALLOW_REDIRECTS=0来阻止重定向。文章指出,如果在容器内部运行,这一点尤为重要。 - 内存预留:vLLM 会根据模型能够接收的最大尺寸媒体提前占用编码器内存。对于不使用的输入,如果像
--limit-mm-per-prompt '{"image":0,"video":0}'这样设为 0,则可以将这部分份额腾给 KV;也可以通过尺寸提示(count,width,height,num_frames) 来减少预留量。 - CPU 内存:用于避免重复处理相同媒体的处理程序缓存默认为 4GiB(
mm_processor_cache_gb)。在设定容器内存上限时必须加上这部分份额,如果这样做没有收益,则可以设为 0 将其关闭。 - 0.32.0 版本更改为仅允许将不受信任的图像作为光栅格式打开(根据发布说明,拒绝 EPS、PostScript、WMF)。
初版中因为需要重新下载视觉语言模型而未作实测,获得批准后我们在下文的 2-1 节进行了测量。
2-1. 2026-10-27 实测:在 8GB 上部署 2B 视觉语言模型
- 环境:单张 8GB 级笔记本 GPU(Ada 架构),vLLM 0.32.0,Qwen3-VL-2B-Instruct(bf16,权重 4.08GiB),
--max-model-len 4096. - 图像:使用 PIL 绘制的 PNG 并转换为 data URL 输入(不接收外部地址)。白底,包含一个彩色矩形和四位数字。尺寸分别为 448×448 和 896×896,各 5 张。
- 提问:每张图像问三次。①“数字是多少”(首次),② 再次询问相同问题,③“矩形的颜色是什么”。贪婪解码。
首先,让它成功启动
| 配置 | KV 剩余内存 | KV 空间 | 结果 |
|---|---|---|---|
默认(--gpu-memory-utilization 0.85) |
-0.07GiB | 启动失败“No available memory for the cache blocks” | |
--limit-mm-per-prompt '{"image":1,"video":0}' 仅增加 |
-0.07GiB | 同样的失败 | |
| 内存比例 0.92 | 0.47GiB | 4,352 个 Token(4,096 Token 请求 1.06 个) | 成功启动 |
0.92 + 尺寸提示 {"image":{"count":1,"width":896,"height":896},"video":0} |
2.13GiB | 19,904 个 Token(4.86 个) | 成功启动 |
vLLM 在启动时会使用“模型能够接收的最大图像”运行一次编码器来测量内存。日志记录显示编码器缓存预算被设为了 16,384 个 Token。加上 4GB 的权重和该预算后,在 8GB 上就没有剩余的 KV 空间了。仅减少图像数量并没有用,而当告知实际要接收的图像大小时,KV 空间变为了原来的 4.5 倍。这是在小 GPU 上部署视觉语言模型时最先需要调整的参数。
结果(带大小提示的服务)
| 项目 | 448×448 | 896×896 |
|---|---|---|
| 提示词 Token(图像 + 问题) | 220 | 808 |
| 新图像的 TTFT 中位数 | 67.9ms | 238.0ms |
| 相同图像,再次询问相同问题 | 31.8ms(缓存 208 个 Token) | 32.7ms(缓存 800 个 Token) |
| 相同图像,不同问题 | 32.1ms(缓存 192 个 Token) | 33.4ms(缓存 784 个 Token) |
- 图像 Token 数量与面积成正比。 当边长翻倍时,Token 数量增至 3.7 倍,首 Token 耗时增至 3.5 倍。
- 再次发送相同的图像会跳过编码器和 Prefill。 前缀缓存将图像哈希混入了块哈希中,使得相同的图像之间可以共享块(即使问题不同,直到图像部分依然可以命中),处理程序缓存指标
vllm:mm_cache_hits_total统计了 30 次中有 20 次(第二次和第三次使用)命中。 - 正确率:颜色 10/10,四位数字 0/10。 数字识别常出现错误,例如将 6082 读作“6002”,将 5435 读作“543”,将 9532 读作“932”,会替换或漏掉某一位。由于无法区分这是 2B 模型的局限性还是预处理的问题,故标记为“未确认”。
SSRF:服务器是否会代为下载
| 请求的图像地址 | 默认服务器 | --allowed-media-domains example.com |
|---|---|---|
http://127.0.0.1:8000/health(服务器自身) |
400 “Failed to load image: cannot identify image file”(5.5ms) | 400 “The URL must be from one of the allowed domains”(2.6ms) |
http://127.0.0.1:8000/metrics |
400,相同消息 | 400,相同拒绝 |
file:///etc/passwd |
500 “Cannot load local files without --allowed-local-media-path」 |
同左 |
默认服务器返回的消息是“这不是图像”。这说明 服务器确实向该内部地址发送了请求并获取了响应。虽然响应内容没有被返回,但请求者可以通过该服务器探测内网,并根据响应时间和错误类型来探知开放了什么端口。指定域名后,系统在下载前就进行了拒绝。本地文件默认处于禁止状态。对于接收图像 URL 的服务来说, --allowed-media-domains 绝非可选配置,而是必需项。
3. 池化:嵌入与重排也使用相同的引擎
不进行生成、而是提取并使用最后隐藏状态的模型,在 vLLM 中被称为 池化模型。 --runner pooling 以 /v1/embeddings, /score, /rerank, /classify, /pooling 启动时,提供的是
方法
- 嵌入:将 Qwen3-Embedding-0.6B 以
--runner pooling启动,并用一个请求发送了 64 个包含 120 个随机单词(约 127 个 Token)的句子。前六次中的第一次为预热。 - 重排:Qwen3-Reranker-0.6B 是一个原本通过「yes」和「no」token 的分数来判断相关性的生成模型。我们按照 vLLM 示例的方法
--hf-overrides '{"architectures":["Qwen3ForSequenceClassification"],"classifier_from_token":["no","yes"],"is_original_qwen3_reranker":true}'和示例的聊天模板,将其作为分类模型运行。我们将一个问题和 32 个短文档(6 种类型循环出现)/rerank发送了过去。
结果
| 任务 | 发起的请求 | 延迟中位数(5次) | 吞吐量 | 备注 |
|---|---|---|---|---|
| Embedding | 64 个句子 × 约 127 个 token | 371.8ms | 每秒 172 个(约 21,900 个 token) | 维度 1,024 |
| 重排 | 1 个问题 + 32 个文档 | 33.9ms | 每秒 944 个 | 匹配的文档得分为 0.993,排在第 1 位 |
- Embedding 的 token 处理速度(约每秒 21,900 个 token)与同一 GPU 上生成模型的 Prefill 速度(第 2 篇,约每秒 18,000 个 token)相似。这是因为 Pooling 仅包含 Prefill 阶段。
- 对于“推理引擎如何重写公共前缀”这一问题,重排将前缀缓存句子的得分排在了最上方(0.993 分)。由于该模型经过了重排任务的训练,因此它能很好地完成这项工作。
- Pooling 服务器也分配了 4.3GiB(40,272 个 token)的 KV 缓存。功能表中提到因果注意力 Pooling 支持分块 Prefill(chunked prefill)和前缀缓存,因此这部分空间得到了利用。如果是仅用于 Embedding 的服务器,可以降低
--gpu-memory-utilization以便将 GPU 用于其他任务(未实际测量)。 - 根据安全文档,像
/score,/rerank,/pooling,/classify这样没有附加/v1的路径,即使提供--api-key也无法通过身份验证。我们在第 8 篇中实际测试了具有相同特性的路径。
4. LoRA 多路服务
这是指在单个基础模型上加载多个 LoRA 适配器,并在每次请求时使用不同适配器的功能(--enable-lora --max-loras --max-lora-rank)。在同款 8GB 级别 GPU 上使用 1.7B 模型测得的数据可以在 LoRA 适配器服务部分找到。使用适配器会使速度变慢 8.5% 至 17.9%,并且当适配器数量超过槽位数量时,吞吐量会急剧下降。本节将补充两点内容。
- 在运行期间加载适配器是一项危险的功能。
VLLM_ALLOW_RUNTIME_LORA_UPDATING=True对于通过/v1/load_lora_adapter开启的 - ,文档警告称:“除非在隔离且完全受信任的环境中,否则不要在生产环境中使用它。”因为这会让请求者决定服务器要读取的路径。 无法与投机解码(Speculative Decoding)同时使用。
在 0.32.0 文档的功能兼容性表中,LoRA 和投机解码的交叉处标记为 ❌(第 3 篇)。
- 5. 未能确认的事项
- 由于只有单张 GPU,TP、PP、DP、EP 的实际收益和通信成本未进行实际测量。
- 视频和音频输入、多张图像输入以及并发请求下的编码器延迟未进行实际测量。数值出现偏差的原因也未进行确认。
Embedding 吞吐量随批次大小(Batch Size)的变化、长文档重排以及准确率评估均未进行实际测量。
- vLLM 文档,Parallelism and Scaling, Data Parallel Deployment, Multimodal Inputs, Conserving Memory, Pooling Models, Security(0.32.0): https://docs.vllm.ai/en/latest/
- Shoeybi 等人,“Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism”,2019. https://arxiv.org/abs/1909.08053
- vLLM 示例,将 Qwen3 重排模型作为分类模型运行:https://github.com/vllm-project/vllm/blob/v0.31.0/examples/pooling/score/qwen3_reranker_online.py
- vLLM 0.32.0 版本说明(允许的图像格式列表):https://github.com/vllm-project/vllm/releases/tag/v0.32.0
- 往期文章:LoRA 适配器服务、NVIDIA Dynamo