关键词 / 建议标签:DGX Spark、GB10、大模型部署、vLLM、SGLang、DeepSeek-V4-Flash、Qwen3.8-27B、NVFP4、TP=2、RoCE、推理性能优化
说明:文中主机名、内网地址、用户名均已做脱敏处理(示例值),命令与参数保持可直接参考的完整性。
1. 引言:为什么用两台 DGX Spark 跑本地大模型
DGX Spark(GB10)是单机 128GB 统一内存的桌面级 AI 算力盒子:单机可以跑 30B 级模型,但要上 200B+ 参数的 MoE(如 DeepSeek-V4-Flash),单机内存不够,必须双机甚至多机。
本文完整记录了我们在一对 DGX Spark 上的两段真实工程实践:
此外还有三个"反直觉"的实测结论:双机不是 2 倍速、换 NVFP4 权重没有免费午餐、GPU 利用率 95% 却只烧 42W —— 这正是 GB10 的带宽天花板。如果你正准备折腾双机,本文的坑单和性能数据可以帮你省掉大量试错。
2. 硬件与网络基线
2.1 设备规格
| SoC | GB10(aarch64,Blackwell 架构 SM121) |
| 内存 | 128 GB LPDDR5x 统一内存(UMA),带宽约 273 GB/s |
| GPU | 每台 1 张 → TP>1 必须跨节点(单机只能 TP=1) |
| 互联 | ConnectX-7,QSFP 口仅以太网模式,每口 200 Gb/s(每物理口 = 2 个逻辑口) |
| 两机连接 | CX7 直连 RoCE(无交换机) |
2.2 高速链路配置要点
| 数据面网卡 | enp1s0f1np1(两端同名,Head 192.168.101.10 / Worker 192.168.101.11,示例值) |
| MTU | 两端必须一致;社区实测配置 9000(本机基线 4200,运行时临时改可生效) |
| RoCE 设备 | rocep1s0f1(两端) |
| RoCEv2 GID 索引 | 必须按节点自动探测(本机实测 Head=3 / Worker=4,且会随重启漂移) |
整机拓扑:
#mermaid-svg-w4dpKtjr3mIz67G9{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-w4dpKtjr3mIz67G9 .error-icon{fill:#552222;}#mermaid-svg-w4dpKtjr3mIz67G9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-w4dpKtjr3mIz67G9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-w4dpKtjr3mIz67G9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-w4dpKtjr3mIz67G9 .marker.cross{stroke:#333333;}#mermaid-svg-w4dpKtjr3mIz67G9 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-w4dpKtjr3mIz67G9 p{margin:0;}#mermaid-svg-w4dpKtjr3mIz67G9 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 .cluster-label text{fill:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 .cluster-label span{color:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 .cluster-label span p{background-color:transparent;}#mermaid-svg-w4dpKtjr3mIz67G9 .label text,#mermaid-svg-w4dpKtjr3mIz67G9 span{fill:#333;color:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 .node rect,#mermaid-svg-w4dpKtjr3mIz67G9 .node circle,#mermaid-svg-w4dpKtjr3mIz67G9 .node ellipse,#mermaid-svg-w4dpKtjr3mIz67G9 .node polygon,#mermaid-svg-w4dpKtjr3mIz67G9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-w4dpKtjr3mIz67G9 .rough-node .label text,#mermaid-svg-w4dpKtjr3mIz67G9 .node .label text,#mermaid-svg-w4dpKtjr3mIz67G9 .image-shape .label,#mermaid-svg-w4dpKtjr3mIz67G9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-w4dpKtjr3mIz67G9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-w4dpKtjr3mIz67G9 .rough-node .label,#mermaid-svg-w4dpKtjr3mIz67G9 .node .label,#mermaid-svg-w4dpKtjr3mIz67G9 .image-shape .label,#mermaid-svg-w4dpKtjr3mIz67G9 .icon-shape .label{text-align:center;}#mermaid-svg-w4dpKtjr3mIz67G9 .node.clickable{cursor:pointer;}#mermaid-svg-w4dpKtjr3mIz67G9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-w4dpKtjr3mIz67G9 .arrowheadPath{fill:#333333;}#mermaid-svg-w4dpKtjr3mIz67G9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-w4dpKtjr3mIz67G9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-w4dpKtjr3mIz67G9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w4dpKtjr3mIz67G9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-w4dpKtjr3mIz67G9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w4dpKtjr3mIz67G9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-w4dpKtjr3mIz67G9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-w4dpKtjr3mIz67G9 .cluster text{fill:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 .cluster span{color:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-w4dpKtjr3mIz67G9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-w4dpKtjr3mIz67G9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-w4dpKtjr3mIz67G9 .icon-shape,#mermaid-svg-w4dpKtjr3mIz67G9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w4dpKtjr3mIz67G9 .icon-shape p,#mermaid-svg-w4dpKtjr3mIz67G9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-w4dpKtjr3mIz67G9 .icon-shape .label rect,#mermaid-svg-w4dpKtjr3mIz67G9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w4dpKtjr3mIz67G9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-w4dpKtjr3mIz67G9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-w4dpKtjr3mIz67G9 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Worker 节点
Head 节点 · 生产/主控
CX7 直连 · RoCE · 单口约 99 Gb/sMTU 两端必须一致
GB10 · 128GB 统一内存
fabric 网卡 enp1s0f1np1192.168.101.10
管理网卡(有线)
服务端口 8000 / 8081
GB10 · 128GB 统一内存
fabric 网卡 enp1s0f1np1192.168.101.11
管理网卡(WiFi)
内网管理链路SSH · 镜像/权重分发
GID 自动探测脚本(写进两端启动脚本,避免硬编码漂移导致的建链失败):
for i in $(seq 0 15); do
t=$(cat /sys/class/infiniband/rocep1s0f1/ports/1/gid_attrs/types/$i 2>/dev/null || true)
g=$(cat /sys/class/infiniband/rocep1s0f1/ports/1/gids/$i 2>/dev/null || true)
if [ "$t" = "RoCE v2" ] && [ -n "$g" ]; then
case "$g" in *ffff:*) NCCL_GID_INDEX=$i; break;; esac
fi
done
链路验证命令:
# 1. RoCE 设备状态(两端 ACTIVE)
rdma link && ibdev2netdev
# 2. RDMA 带宽实测(应接近 100 Gb/s / 单逻辑口)
ib_write_bw 192.168.101.11 -d rocep1s0f1 –report_gbits -q 4 -R –force-link IB
# 3. MTU 一致性
ip link show enp1s0f1np1 | head -1
ping -M do -s 8972 -c 3 192.168.101.11 # 计划用 9000 MTU 时先验证不丢包
# 4. 关键:确认 NCCL 真的走了 IB,而不是静默退化为 TCP
# 容器日志中应有 NET/IB 字样;缺失则 /dev/infiniband 未透传(需 –privileged)
实测数据:ib_write_bw 4 QP 平均 99.37 Gb/s(单逻辑口,QP-MTU 4096)。要跑满 200G 需第二逻辑口配 IP + 双 HCA(NCCL_IB_HCA=rocep1s0f1,roceP2p1s0f1)。
为什么反复强调通信层? 社区案例显示:容器缺 /dev/infiniband 时 NCCL 会无任何报错地退化为 TCP,带宽从 ~9.8 GB/s 掉到 ~2.1 GB/s,推理吞吐 65.4 → 34.6 tok/s。这个坑不看 NCCL_DEBUG=INFO 根本发现不了。
3. 第一段:Qwen3.8-27B 单机生产部署与优化(SGLang)
3.1 生产配置
| 引擎 | SGLang(自构建镜像,DFlash2 已并入上游) |
| 主模型 | RadixArk/Qwen3.8-27B-NVFP4(约 21 GB) |
| 投机草稿 | z-lab/Qwen3.8-27B-DFlash2(约 3.6 GB,无损投机) |
| 上下文 | 262,144(256K,模型原生) |
| KV Cache | fp8_e4m3 |
| 显存比例 | –mem-fraction-static 0.50 |
| 并发上限 | –max-running-requests 8 |
| 服务 | 8000 端口,OpenAI 兼容,无鉴权(内网直连) |
启动命令(核心参数):
docker run -d –name qwen38-sglang-single –restart unless-stopped –gpus all \\
–memory 100g –memory-swap 100g –shm-size 16g –network host –ipc=host \\
-e TORCHINDUCTOR_CACHE_DIR=/cache/inductor -e HF_HUB_OFFLINE=1 \\
-e SGLANG_OPT_MAMBA_SKIP_DECODE_LOCK=1 \\
-v /home/spark/.config/qwen38/sglang-cache:/cache \\
-v /home/spark/.cache/huggingface:/root/.cache/huggingface \\
-v /home/spark/.config/qwen38:/out \\
qwen38-dflash2:v1.2.3 python3 -m sglang.launch_server \\
–tp-size 1 –context-length 262144 –mem-fraction-static 0.50 \\
–host 0.0.0.0 –port 8000
3.2 两个有效的优化项(实测有效)
(1)权重加载 I/O 优化:GB10 是统一内存,mmap 随机读 4KB 页会拖慢加载,且 page cache 会挤占 KV 池。加两个 SGLang 参数:
–weight-loader-prefetch-checkpoints # 4 线程顺序预读 checkpoint 进 page cache
–weight-loader-drop-cache-after-load # 加载完丢弃 page cache,归还约 24GB 给系统
(2)mamba 解码免锁:Qwen3.8 是 mamba 混合架构,大 prompt 场景曾出现挂起,环境变量 SGLANG_OPT_MAMBA_SKIP_DECODE_LOCK=1 消除该问题。
优化前后对比(实测):
| 权重加载(主模型) | 141.5 s | 107.0 s | -24% |
| 权重加载(含 draft) | 170.4 s | 126.1 s | -26% |
| 引擎启动总计 | 427.2 s | 379.3 s | -11% |
| 加载后系统 buff/cache | 33 GB | 9 GB | -24 GB |
| KV 池容量 | 413,280 tok | 406,553 tok | 持平(噪声内) |
3.3 性能与质量基线
| 短提示解码(greedy 中位) | 56.0 tok/s |
| 32K 长上下文解码 | 34–38 tok/s(受内容相关接受率影响,波动大) |
| TTFT(短提示) | 0.205 s |
| 长上下文检索 | 30K / 60K 深度 needle 精确取回(15.0 s / 29.2 s) |
| 质量探针 | 已知答案 6/6 通过;tool-call 解析正常 |
经验:投机解码(DFLASH2)的接受率高度依赖内容类型——code 最高、散文最低,因此"同一模型不同负载"的吞吐差异可达 2 倍,做基准测试必须按负载类型分桶。
4. 第二段:Qwen3.8-27B 双机 TP=2 验证(SGLang)
4.1 双机能带来什么:先看社区数据
| Code 生成 | 52–61 tok/s | 87 tok/s |
| Prose | 26 | 41 |
| Thinking 对话 | 34–49 | 49 |
| Prefill | — | 约 10K tok/s |
| 8 并发聚合 | 135–148 tok/s | 134–148 tok/s(持平) |
结论直言:双机是 1.4–1.6x 而非 2x,收益集中在单请求吞吐与并发余量;如果是纯高并发场景,单机 DP + 网关负载均衡可能更划算。
4.2 三条技术路线怎么选
| A. SGLang 手工双机 | –tp 2 –nnodes 2 –node-rank 0/1 –dist-init-addr | 社区双机主流、实测最优,推荐 |
| B. eugr 编排 | spark-vllm-docker 的 run-recipe.sh | 自动配 rank,手工全 hang 时它能通 |
| C. vLLM + Ray | 官方 playbook | 官方支持但实测吞吐约为 SGLang 一半 |
4.3 双机八大坑(浓缩版)
| Worker 崩溃循环 DistNetworkError | 对端未就绪 / 建链失败 | 严格先 Worker 后 Head |
| NCCL 静默 hang 无报错 | 绑定了多 IP 网卡抓错 IP / MTU 两端不一致 | 控制面指向单一 IP 网卡;统一 MTU |
| ibv_modify_qp EINVAL,一侧起不来 | GID index 硬编码,重启后漂移 | 启动时自动探测 RoCEv2 IPv4 GID |
| 吞吐只有单机水平 | NCCL 静默走了 TCP | –privileged 透传 /dev/infiniband;NCCL_DEBUG=INFO 对拍 |
| 参数不一致导致死锁 | 两 rank 参数必须逐字节一致 | 同一脚本、仅 –node-rank 不同 |
| max_tokens ≤ 8 请求打挂全集群 | DFlash2 draft=8 边界缺陷 | 客户端约定不发 ≤8;或补丁把 <16 钳到 16 |
| 客户端中断流请求导致崩溃 | 投机解码 async generator 边界 | –watchdog-timeout 120 + cron 自愈 |
| 重启要 6–8 分钟 | 权重重载 + CUDA graph 捕获 | 接受现实,用自愈守护把"死集群"变成"几分钟抖动" |
4.4 停机窗口实测结果
三轮配置迭代后的最终数据(NVFP4 + DFlash2 + FP8 KV,512K 上下文):
| 单流 code / math | 52.0 / 54.7 tok/s | 单机基线 54.8 / 58.5(持平) |
| 单流 prose | 23.7 tok/s | 单机 ~26(略低) |
| code c4 聚合 | 163.5 tok/s | — |
| code c8 聚合 | 198.3 tok/s | 单机 8 并发 135–148 |
| 服务端批次峰值 | 251.6 tok/s(接受率 0.48) | — |
跨节点通信全程确认 NET/IB,CUDA graph 生效。
最终决策:维持单机生产。TP=2 功能验证通过、code 高并发有 +35–45% 收益,但需额外承担崩溃防护、端口切换与运维复杂度;TP=2 栈保留为已验证的备用/扩容路径。
5. 第三段:DeepSeek-V4-Flash-0731 双机部署(vLLM TP=2)
5.1 模型与栈
| 模型 | deepseek-ai/DeepSeek-V4-Flash-0731 |
| 架构 | 284B 总参 / 13B 激活 MoE,43 层,256 路由专家 + 1 共享(每 token 激活 6 个) |
| 精度 | FP4 专家 + FP8 其余(预训练即混合精度,检查点 156 GB / 48 分片) |
| 投机解码 | DSpark(模型内置 Markov 头,配置中 dspark_markov_rank 256) |
| 上下文 | 原生 1,048,576(YaRN factor 16) |
| 引擎 | vLLM 定制镜像(eugr/spark-vllm-b12x,B12X 内核,面向 SM121) |
| 并行 | TP=2 + –nnodes 2(launcher 的多机 no-Ray 模式) |
| 服务 | 8000 端口,模型名 DeepSeek-V4-Flash |
推理架构与数据流:
#mermaid-svg-j6U4HAQS1XXVfKYF{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-j6U4HAQS1XXVfKYF .error-icon{fill:#552222;}#mermaid-svg-j6U4HAQS1XXVfKYF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-j6U4HAQS1XXVfKYF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-j6U4HAQS1XXVfKYF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-j6U4HAQS1XXVfKYF .marker.cross{stroke:#333333;}#mermaid-svg-j6U4HAQS1XXVfKYF svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-j6U4HAQS1XXVfKYF p{margin:0;}#mermaid-svg-j6U4HAQS1XXVfKYF .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF .cluster-label text{fill:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF .cluster-label span{color:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF .cluster-label span p{background-color:transparent;}#mermaid-svg-j6U4HAQS1XXVfKYF .label text,#mermaid-svg-j6U4HAQS1XXVfKYF span{fill:#333;color:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF .node rect,#mermaid-svg-j6U4HAQS1XXVfKYF .node circle,#mermaid-svg-j6U4HAQS1XXVfKYF .node ellipse,#mermaid-svg-j6U4HAQS1XXVfKYF .node polygon,#mermaid-svg-j6U4HAQS1XXVfKYF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-j6U4HAQS1XXVfKYF .rough-node .label text,#mermaid-svg-j6U4HAQS1XXVfKYF .node .label text,#mermaid-svg-j6U4HAQS1XXVfKYF .image-shape .label,#mermaid-svg-j6U4HAQS1XXVfKYF .icon-shape .label{text-anchor:middle;}#mermaid-svg-j6U4HAQS1XXVfKYF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-j6U4HAQS1XXVfKYF .rough-node .label,#mermaid-svg-j6U4HAQS1XXVfKYF .node .label,#mermaid-svg-j6U4HAQS1XXVfKYF .image-shape .label,#mermaid-svg-j6U4HAQS1XXVfKYF .icon-shape .label{text-align:center;}#mermaid-svg-j6U4HAQS1XXVfKYF .node.clickable{cursor:pointer;}#mermaid-svg-j6U4HAQS1XXVfKYF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-j6U4HAQS1XXVfKYF .arrowheadPath{fill:#333333;}#mermaid-svg-j6U4HAQS1XXVfKYF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-j6U4HAQS1XXVfKYF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-j6U4HAQS1XXVfKYF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-j6U4HAQS1XXVfKYF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-j6U4HAQS1XXVfKYF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-j6U4HAQS1XXVfKYF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-j6U4HAQS1XXVfKYF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-j6U4HAQS1XXVfKYF .cluster text{fill:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF .cluster span{color:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-j6U4HAQS1XXVfKYF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-j6U4HAQS1XXVfKYF rect.text{fill:none;stroke-width:0;}#mermaid-svg-j6U4HAQS1XXVfKYF .icon-shape,#mermaid-svg-j6U4HAQS1XXVfKYF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-j6U4HAQS1XXVfKYF .icon-shape p,#mermaid-svg-j6U4HAQS1XXVfKYF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-j6U4HAQS1XXVfKYF .icon-shape .label rect,#mermaid-svg-j6U4HAQS1XXVfKYF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-j6U4HAQS1XXVfKYF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-j6U4HAQS1XXVfKYF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-j6U4HAQS1XXVfKYF :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
TP=2 集合通信RoCE NET/IB(占用 <2%)
客户端OpenAI 兼容请求
Head · APIServer :8000
EngineCore rank0权重分片约 78GB
Worker · EngineCore rank1权重分片约 78GB
KV 池 fp8_ds_mla57万+ tokens / 256K 上下文
DSpark 投机解码内置 Markov 头 · n=3
流式输出短提示约 73 tok/s
5.2 镜像获取的坑(docker 代理失效)
| docker pull 全部失败(EOF 重试) | docker daemon 配了已失效的本地代理,且 registry 被污染解析 | 自研 OCI 拉取脚本走 docker.m.daocloud.io 镜像源直连(单流 6.1 MB/s,4 并发 ~75 MB/s),docker load 导入 |
| Worker 无法拉取镜像 | 镜像源对 Worker 不可达 | Head docker save 后经 CX7 分发 |
5.3 两套配置与实测性能
# 配置A:200K 上下文 + fp8_ds_mla KV + DSpark
./run-recipe.sh recipes/deepseek-v4-flash-0731.yaml \\
–max-model-len 200000 –gpu-mem 0.85 -e HF_HUB_OFFLINE=1 -d
# 配置B:1M 上下文 + DSpark(关闭 CUDA graph 内存估算以最大化 KV 池)
./run-recipe.sh recipes/deepseek-v4-flash-0731.yaml \\
–max-model-len 1000000 –gpu-mem 0.88 –max-cudagraph-capture-size 16 \\
-e HF_HUB_OFFLINE=1 -e VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0 -d
启动耗时:权重加载约 76 s(InstantTensor 快速加载)、CUDA graph 捕获约 25 s,从启动到 API 就绪约 5 分钟。
配置 A(200K / 350,026 tokens KV 池):
| code 解码 | 53.7 tok/s | 72.1 | 121.4 |
| prose 解码 | 37.4 | 48.1 | 78.1 |
| TTFT | 0.33–0.65 s | 0.67–3.45 s | 0.65–1.18 s |
长上下文:60K/120K/180K 深度 needle 6/6 通过(180K 预填 128–163 s,约 1.2K tok/s)。
配置 B(1M / 1,774,482 tokens KV 池):
| code 解码 | 72.9 tok/s | 95.7 | 56.1(单流降至 14.4) |
| prose 解码 | 38.3 | 52.5 | 37.1 |
| 250,000 | 精确检索 OK | 131.8 s |
| 600,000 | 精确检索 OK | 403.1 s |
| 950,000 | 精确检索 OK | 885.7 s |
注意:并发 4 时吞吐显著退化,原因是投机解码在 decode 阶段的验证批次膨胀(num_speculative_tokens=5),生产建议并发 ≤2。
5.4 三个栈限制(实测确认,非配置问题)
| NVFP4 KV(nvfp4_ds_mla) | 不可用 | 镜像内校验要求 GLM5Next/GLM-5.2 架构;DeepSeek-V4 走 fp8_ds_mla |
| MTP 投机(method=mtp) | 不可用 | 检查点草稿键为 mtp.{0..n}.*,经典 MTP 加载器按 model.layers.{N}.* 取键 → KeyError;DSpark 是唯一可用投机路径 |
| Decode Context Parallel(DCP) | 不可用 | NotImplementedError: B12X compressed sparse MLA does not support DCP |
6. 性能深挖:压测、利用率与调参(本文独家数据)
生产切到 256K 配置后,我们做了一组系统性测试。
6.1 KV 池并发压测(冷启动,每请求独立长提示)
方法:流式请求 + 每 2 秒采样 /metrics(KV 占用、排队数),提示词每次随机生成以杜绝前缀缓存干扰。
32K 提示:
| 1 | 14.1 s | 29.8 tok/s | 1,883 tok/s | 0 | 35.8% |
| 2 | 21.6 s | 12.7 | 2,115 | 1 | 36.6% |
| 4 | 36.3 s | 3.9 | 2,102 | 3 | 38.2% |
| 8 | 63.4 s | 1.9 | 2,198 | 7 | 40.5% |
128K 提示:
| 1 | 59.7 s | 27.2 tok/s | 2,092 tok/s | 0 | 37.8% |
| 2 | 90.4 s | 11.3 | 2,143 | 1 | 40.5% |
| 4 | 150.4 s | 1.5 | 2,175 | 3 | 43.2% |
三个结论:
6.2 意外发现:prefix cache 能扛 10 小时
对照实验里,我们用了与 10 小时前完全相同的提示词:32K 的 TTFT 从 14 s 降到 0.69 s,128K 从 60 s 降到 0.97 s。该实例的 KV 缓存跨小时保留了长文档块。业务启示:固定系统提示 / 固定文档前缀的场景,长文 TTFT 是亚秒级——这是长文场景零成本的最大提速手段。
6.3 硬件利用率实测:95% 却只烧 42W
| 空闲 | 0% | 0% | 11.6 W | 2411 MHz | 0 |
| 解码(生成 238 token) | 94.8% | 94.8% | 29.7 W | 2409 MHz | 0.07 GB/s |
| 长文预填(64K) | 95.8% | 93.4% | 42.2 W | 2397 MHz | 0.60 GB/s(链路 2%) |
解读:GPU 利用率接近 100% 但功耗极低——典型的内存带宽瓶颈特征:SM 显示"忙",其实在等数据。GB10 的 LPDDR5X 带宽(273 GB/s)只有 H100 HBM(3.35 TB/s)的约 1/12,而 MoE 推理是带宽敏感负载。
定量验证:13B 激活 × ~0.5 字节 ≈ 每节点每 token 约 3.6 GB 权重流量 → 273 GB/s 的理论天花板约 75 tok/s,实测短上下文 73 tok/s,已到带宽的 93%+。而且双机互联完全不是瓶颈(TP 通信只占 RoCE 链路 2% 以下)。
6.4 调参 A/B:max-num-batched-tokens 8192 vs 16384(反面教材)
| 32K 提示 TTFT | 14.1 s | 43.2 s | 劣化 3 倍 |
| 128K 提示 TTFT | 59.7 s | 70.7 s | 劣化 |
| KV 池 | 674,512 | 279,228(-59%) | 劣化 |
| 峰值激活内存 | 2.36 GiB | 3.78 GiB | 代价 |
结论:批预算翻倍换不来预填收益,反而压缩 KV 池,此路不通,保持 8192。
6.5 稳定性:投机解码深度 5 → 3 治好了"挂死"
现象:num_speculative_tokens=5 时,长生成请求偶发 worker 挂死——生成速率突然归零 → 连续报"引擎 60 秒无输出" → 约 4 分钟后 RPC call to sample_tokens timed out → 引擎自毁,客户端收到 500。一晚复现两次。
#mermaid-svg-AvY99lnDMpX6q7Lm{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AvY99lnDMpX6q7Lm .error-icon{fill:#552222;}#mermaid-svg-AvY99lnDMpX6q7Lm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AvY99lnDMpX6q7Lm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AvY99lnDMpX6q7Lm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AvY99lnDMpX6q7Lm .marker.cross{stroke:#333333;}#mermaid-svg-AvY99lnDMpX6q7Lm svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AvY99lnDMpX6q7Lm p{margin:0;}#mermaid-svg-AvY99lnDMpX6q7Lm .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm .cluster-label text{fill:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm .cluster-label span{color:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm .cluster-label span p{background-color:transparent;}#mermaid-svg-AvY99lnDMpX6q7Lm .label text,#mermaid-svg-AvY99lnDMpX6q7Lm span{fill:#333;color:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm .node rect,#mermaid-svg-AvY99lnDMpX6q7Lm .node circle,#mermaid-svg-AvY99lnDMpX6q7Lm .node ellipse,#mermaid-svg-AvY99lnDMpX6q7Lm .node polygon,#mermaid-svg-AvY99lnDMpX6q7Lm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AvY99lnDMpX6q7Lm .rough-node .label text,#mermaid-svg-AvY99lnDMpX6q7Lm .node .label text,#mermaid-svg-AvY99lnDMpX6q7Lm .image-shape .label,#mermaid-svg-AvY99lnDMpX6q7Lm .icon-shape .label{text-anchor:middle;}#mermaid-svg-AvY99lnDMpX6q7Lm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AvY99lnDMpX6q7Lm .rough-node .label,#mermaid-svg-AvY99lnDMpX6q7Lm .node .label,#mermaid-svg-AvY99lnDMpX6q7Lm .image-shape .label,#mermaid-svg-AvY99lnDMpX6q7Lm .icon-shape .label{text-align:center;}#mermaid-svg-AvY99lnDMpX6q7Lm .node.clickable{cursor:pointer;}#mermaid-svg-AvY99lnDMpX6q7Lm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AvY99lnDMpX6q7Lm .arrowheadPath{fill:#333333;}#mermaid-svg-AvY99lnDMpX6q7Lm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AvY99lnDMpX6q7Lm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AvY99lnDMpX6q7Lm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AvY99lnDMpX6q7Lm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AvY99lnDMpX6q7Lm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AvY99lnDMpX6q7Lm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AvY99lnDMpX6q7Lm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AvY99lnDMpX6q7Lm .cluster text{fill:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm .cluster span{color:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AvY99lnDMpX6q7Lm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AvY99lnDMpX6q7Lm rect.text{fill:none;stroke-width:0;}#mermaid-svg-AvY99lnDMpX6q7Lm .icon-shape,#mermaid-svg-AvY99lnDMpX6q7Lm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AvY99lnDMpX6q7Lm .icon-shape p,#mermaid-svg-AvY99lnDMpX6q7Lm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AvY99lnDMpX6q7Lm .icon-shape .label rect,#mermaid-svg-AvY99lnDMpX6q7Lm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AvY99lnDMpX6q7Lm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AvY99lnDMpX6q7Lm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AvY99lnDMpX6q7Lm :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
长生成请求n=5 投机解码
生成速率突然归零worker 挂死
连续告警引擎 60s 无输出
约 4 分钟后RPC sample_tokens 超时
EngineDeadError客户端收到 500
处置:投机深度 5 → 3
10 小时 / 117 次请求0 错误 0 挂死
处置:投机深度降到 3 后,连续 10 小时、117 次真实请求 0 错误、0 挂死。代价是吞吐略降;若追求极限速度可调回 5,但要接受挂死风险。
6.6 顺带辟谣:换 NVFP4 权重不是免费午餐
很多人(包括我们最初)以为"换 NVFP4 权重 = 带宽减半 = 速度翻倍"。核实后发现:
- 官方 0731 检查点的专家层本来就是 FP4(FP4 专家 + FP8 其余),换 NVIDIA 的 nvidia/DeepSeek-V4-Flash-NVFP4(168 GB,ModelOpt 再量化)并不会减小权重体积;
- 解码已贴着 273 GB/s 的带宽天花板(见 6.3),换任何 4-bit 格式都突破不了物理带宽;
- 且该变体不支持 FP8 专属的 deep_gemm_mega_moe 内核,MoE 要走默认后端,可能反而更慢。
7. 运维要点与踩坑清单
7.1 常用运维命令
# 实时滚动日志(双机合并,带节点前缀)
{ docker logs -f –tail 50 vllm_node 2>&1 | sed 's/^/[head] /' & \\
ssh spark@192.168.101.11 'docker logs -f –tail 50 vllm_node 2>&1' | sed 's/^/[worker] /' & wait; }
# 只看错误信号
docker logs -f vllm_node 2>&1 | grep -E –line-buffered "ERROR|Traceback|EngineDeadError|500"
# 实时负载(KV 占用 / 排队 / 运行中请求)
watch -n 2 "curl -s http://127.0.0.1:8000/metrics | grep -E 'num_requests_(running|waiting)|kv_cache_usage_perc'"
# 就绪判断:容器 Up ≠ 服务可用,必须等健康检查通过(大模型加载需数分钟)
curl -s -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8000/health
7.2 高频故障速查
| 端口不可达 | 容器 Up?→ 端口监听?→ 本机回环 → 外部连通性/防火墙 |
| 启动慢/疑似卡住 | 首次 7–9 分钟、常规 5–7 分钟属正常;用 docker logs -f 看进度(权重加载→图捕获→就绪) |
| 客户端报 500 | 查引擎是否 EngineDeadError(见 6.5 挂死链),确认后重启服务 |
| 客户端报模型不存在 | 请求体 model 必须与 –served-model-name 完全一致 |
| OOM / 系统换页 | UMA 下权重+KV+系统共享 128GB;mem-fraction 取 0.5–0.9 区间实测,勿盲目调高(社区有 0.95 整机硬重启案例) |
| 想切回另一个模型 | 双机服务与单机服务互斥(都占满统一内存):先 docker rm -f 当前服务,再启动另一个 |
7.3 GB10 统一内存注意事项
- nvidia-smi 查不到显存占用、cudaMemGetInfo 数值失真——以容器内日志与实测为准;
- 权重加载后会残留大量 page cache,会挤占 KV 池,建议用引擎的 drop-cache 选项(见 3.2)主动归还;
- 容器内存上限(如 –memory 100g)与 gpu-memory-utilization 要一起算账,否则会触发 OOM 或被系统换页拖慢。
8. 结论与选型建议
8.1 一句话结论
- 单机 Qwen3.8-27B:短提示 56 tok/s、256K 上下文,稳定承担日常生产;优化项(加载 I/O、mamba 免锁)实测有效;
- 双机 Qwen3.8-27B(TP=2):功能验证通过,code 高并发 +35–45%,但单流无增益(1.4–1.6x 上限),列装为备用/扩容路径;
- 双机 DeepSeek-V4-Flash-0731:284B MoE 跑通,短提示 73 tok/s、1M 上下文长文检索通过,是当前双机的"重载主力";
- 硬件天花板:GPU 利用率 95%、功耗 42W、解码触及 273 GB/s 带宽上限——参数层面已无优化空间,瓶颈在 GB10 的 LPDDR5X 带宽。
8.2 选型建议表
| 日常对话/轻量 agent | 单机 30B 级(Qwen3.8-27B) |
| 重载编程/长文档分析 | 双机 DeepSeek-V4-Flash(256K–1M) |
| 高并发 code 场景 | 双机 Qwen3.8 TP=2(c8 聚合 198 tok/s)或单机多实例+负载均衡 |
| 长文交互 | 并发 ≤2–3;复用固定前缀吃 prefix cache(TTFT 秒级) |
| 想再提速 | 优先排查前缀复用与并发策略;权重格式/调参已无空间 |
8.3 三条最重要的工程经验
9. 参考资料
官方文档与工具:
- Connect Two Sparks Playbook(NVIDIA 官方组网指南)
- DGX Spark Clustering / ConnectX-7 网络
- SGLang Server Arguments
- vLLM Parallelism and Scaling
- spark-vllm-docker(eugr,含 DeepSeek-V4-Flash 双机 recipe)
社区实测(本文引用的对比数据来源,均已标注):
- Qwen3.8-27B (NVFP4) on Single/Dual DGX Spark — SGLang + DFlash2
- 4× DGX Spark Qwen3.8-27B + 8 pitfalls
- Comprehensive Qwen3.8-27B Study on DGX Sparks
- DeepSeek V4 Flash on two GB10 nodes — tuning and benchmark notes
- RoCE 2× Inference Throughput(NCCL 静默退化 TCP 案例)
免责声明:本文性能数据为在特定硬件(2× DGX Spark GB10)、特定软件版本下的实测结果,不同环境(内核、驱动、镜像版本)可能有差异;社区引用数据已在文中标注来源,仅供对比参考。
完





