单台DGX Spark部署两个Qwen3模型的算力账本
速览
本文探讨了在单台NVIDIA DGX Spark设备上同时部署两个Qwen3大模型的技术可行性。文章详细计算了模型权重、激活值及KV缓存所需的显存资源,并分析了推理时的算力瓶颈。这一实践为边缘侧或低成本环境下高效运行多模型提供了重要的参考数据。
AI 深度解读
单张 DGX Spark 运行两个 Qwen3 模型:本地大模型部署的显存驻留数学
背景
作者的工作站运行着基于 Hermes 的智能体(Agent)栈,而模型推理则部署在同一局域网内的 NVIDIA DGX Spark 设备上。这种架构设计的初衷是保持工作站的响应能力,由 Spark 承担 GPU 计算任务,两者通过 HTTP 代理进行通信。
随着通过 Clawrium 管理的智能体集群规模扩大,并发流量激增,原本“单笔记本、单模型”的简单配置已演变为“小型智能体集群对抗单一后端”的复杂场景。传统的单一模型服务器架构已无法有效应对这种负载形态。
此前,作者使用 ollama 在 Spark 上服务模型。虽然配置简单,但 ollama 独占显卡,缺乏针对每个进程的显存预算控制(如 gpu_memory_utilization 参数),也无法轻松实现重型推理模型与快速响应模型的共存(Co-residency)。其底层的 llama.cpp 后端不支持 PagedAttention,KV 缓存管理方式较为原始。
为了解决上述问题,作者转向使用 vLLM。vLLM 引入了 PagedAttention 以回收 KV 块而非连续固定内存,并通过 gpu_memory_utilization 提供容器级别的显存预算。这使得在一块拥有 119.67 GiB 统一内存的 GB10 芯片(DGX Spark)上,通过 LiteLLM 代理在端口 4000 运行多个 vLLM 容器成为可能。目标是同时驻留两个 Qwen3 模型:使用 Qwen3-Next-80B-Instruct-FP8 处理重型推理任务,使用 Qwen3-4B-Instruct-2507 处理快速交互,并通过单一端点路由。
核心内容
尝试一:信任目标参数的陷阱
作者首先尝试配置 80B 模型,设置 gpu_memory_utilization: 0.75,max_model_len: 65536,max_num_seqs: 4。然而,vLLM 的 KV 缓存初始化崩溃,报错“No available memory for the cache blocks”。由于 Qwen3-Next 主要基于 Mamba 架构,其每页对齐机制导致 KV 池需求高于权重加载后剩余的约 14 GiB 内存。
将利用率提升至 0.85 后,再次崩溃,报错指出设备上的可用内存(98.51/119.67 GiB)小于期望的 GPU 显存利用率(0.85,即 101.72 GiB)。此时,4B 模型已驻留,占用约 16 GiB。问题在于,80B 模型的 0.85 目标是基于整张卡计算的,而非基于剩余可用内存。
关键教训:gpu_memory_utilization 是总 GPU 显存的比例,而非剩余显存的比例。两个共存的 vLLM 进程,其比例之和需低于约 0.95,以留出 CUDA 框架开销的空间。如果误以为这是基于剩余内存的比例,会导致 OOM(内存溢出)或静默的 KV 缓存饥饿。
最终,作者将 80B 模型配置调整为 0.80 / 32k / 2,成功加载。其 KV 池在权重加载后占用约 20.8 GiB。
尝试二:Hermes 智能体集成与模型模式问题
当 Hermes 智能体上线后,工具调用返回为纯文本,JSON 包裹在 content 字段中,tool_calls 为空,finish_reason 为 stop。Hermes 未能执行工具调用。
经过排查,解析器逻辑无误,问题在于模型输出。tool_choice: "required" 有效,但 tool_choice: "auto" 返回空值。Qwen3-Next-80B-Thinking 模型仅支持思维模式(Thinking Mode)。在该检查点中,enable_thinking: false 是结构性无操作,提示词中的 /no_think 也被忽略。模型在 <think> 标签内推理并得出结论,但从不发出工具调用。
对于默认使用 tool_choice: "auto" 的智能体 SDK 而言,这是一个不可恢复的失败。解决方案不是修改解析器标志,而是将 80B 主干模型从 Thinking 版本切换为 Instruct 版本。
作者预拉取了 77 GiB 的 Instruct 版本模型,清空 GPU,使用 --enable-auto-tool-choice --tool-call-parser hermes 启动,不使用 --reasoning-parser。三个 LiteLLM 别名(writer/reviewer/sources)均能干净地通过 tool_choice: "auto" 并返回 finish_reason: tool_calls。代价是:Reviewer 智能体失去了原生的 <think> 追踪,推理过程被移入提示词中。
尝试三:打破共存的参数调整
Reviewer 智能体需要 64k 上下文,作者将 80B 模型参数调整为 0.85 / 65536 / 2。80B 模型加载健康,但 4B 模型进入重启循环 19 次,报错:“设备上的可用内存(12.58/119.67 GiB)小于期望的 GPU 显存利用率(0.12,即 14.36 GiB)”。
此时,80B 模型在 0.85 配置下的实际驻留内存为 101.5 GiB,加上约 5 GiB 的 CUDA 框架开销,仅剩约 12.5 GiB 可用。而 4B 模型需要 14.36 GiB,空间不足。
作者将 80B 调回 0.80,并将 4B 调整为 0.10 / 16384 / 8。两者均健康启动。由于 4B 的 0.10 分配仅留出约 3.5 GiB 给 KV 池,32k 单序列 KV 需求(约 4.8 GiB)无法容纳,因此必须将 max_model_len 降至 16k(约 2.4 GiB)。
驻留数学(The Residency Math)
作者总结了实际观察到的三个关键现象:
- 缓冲区的必要性:80B 模型在 0.80 配置下的实际驻留内存比分配值少 8 GiB。这 8 GiB 的缓冲区是防止 4B 模型因重启波动而破坏部署的唯一原因。在 0.85 配置下,该缓冲区变为负数。
- 小模型的隐性开销:4B 模型在 0.10 配置下实际驻留 13.8 GiB,而非目标暗示的 12 GiB。CUDA 框架开销在小分配中并不会消失。
- Mamba 架构的 KV 特性:在 Qwen3-Next 中,
max_model_len × max_num_seqs主要受 Mamba 状态对齐主导,而非注意力机制的 KV。减半max_model_len并不会像纯注意力模型那样减半 KV 池需求。规划 KV 时应针对 Mamba 页大小,而非基于 Llama 类模型的直觉。
关键要点
gpu_memory_utilization的定义:这是vLLM在进程启动时针对总显存快照的比例,而非剩余显存。- 共存策略:共存进程之间不进行协商,而是竞争资源。前一次失败的尝试留下的 CUDA 上下文可能会暂时增加驻留内存,导致检查误触发。
- 实际驻留 > 目标分配:唯一重要的数字是两个进程稳定后的实际驻留内存,需针对难以重启的模型所需的恢复余量进行测量。目标分配仅是规划输入,实际测量才是真理。
- 部署操作手册: 1.
