概述
本项目在两台 NVIDIA GB10 DGX Spark 系统上本地运行 DeepSeek V4.1 Flash。项目使用 2.9 bits-per-weight 的 EXL3 检查点,在两个节点之间启用张量并行,使用原生 sm_121a 内核,并在端口 8888 上提供兼容 OpenAI 的 vLLM API。
运行中的模型标识符为 DeepSeek-v4.1-Flash-EXL3。该部署仅支持文本,专为通过 CX7 连接的双节点 GB10 套件设计。
容器在 vllm/vllm-openai:deepseekv41-flash-0909 上叠加 EXL3 支持。这是一个 ARM64 vLLM 镜像,其中包含所需的 DeepseekV41 架构。
项目提供的功能
- 本地兼容 OpenAI 的服务:应用可以调用标准的
/v1/chat/completions端点。 - 双节点张量并行:模型通过 CX7 连接以张量并行大小 2 运行。
- EXL3 量化:检查点平均为 2.9 bpw,并使用带有逐张量量化设置的
mul1码本。 - 原生 DSpark 推测:草稿专家已经存储在检查点的
mtp.*下,因此无需单独的草稿模型。 - 长上下文支持:随附配置将
MAX_MODEL_LEN=600000,并使用 2.5 GiB 的 KV 缓存池。 - 文件支持的 Engram 表:未量化的 Engram 数据从原始模型分片 47 和 48 中读取,而不是固定在主机内存中。
- 自动启动:
start.sh会下载权重、获取或构建镜像、与工作节点共享文件、执行 GPU 检查并启动 API。 - 运行时保护:启动器会执行启动余量检查,在预填充阶段之间释放分配器,加载后清除页缓存,并调整容器的 OOM 分数。
硬件和存储要求
本仓库的目标环境恰好是两台 NVIDIA GB10 DGX Spark 节点。默认头节点地址为 10.0.0.1,节点通过 CX7 通信。README 中列出的已测试接口为:第一台节点上的 enp1s0f1np1/rocep1s0f1,以及第二台节点上的 enp1s0f0np0/rocep1s0f0。
请预留约 387 GiB 的模型数据空间:
- 来自 Mia-AiLab/DeepSeek-V4.1-Flash-EXL3-2.9bpw 的 39 个 EXL3 分片约占 197 GiB。
- 原始 DeepSeek 分片 47 和 48 及索引约占 190 GiB,其中包含原生 Engram 表。
- 已发布的容器镜像约占 9 GiB;如果可以拉取匹配镜像,则无需构建。
如果已有完整的 DeepSeek-V4.1-Flash 目录,请将 ENGRAM_DIR 指向该目录,以免再次下载 Engram 源文件。此用途只会读取分片 47 和 48 以及索引。
在 DGX Spark 上,主机 RAM 同时也是 GPU 内存。每次 CUDA 分配都会立即占用共享内存,因此应分别调整存储和内存设置,并使用长提示词进行测试。
准备头节点
1. 克隆仓库并创建环境文件
请在头部 Spark 的仓库目录中运行部署:
cp .env.example .env
如果 .env 不存在,启动器也会在首次运行时创建该文件。请检查文件内容,并在系统有要求时更新 WORKER_USER 和 GID 等值。
.env 中明确命名的值优先于命令行环境变量前缀,因为 start.sh 会使用该文件覆盖继承的环境变量。例如,应在 .env 中修改 WEIGHT_SYNC,而不是运行 WEIGHT_SYNC=zfs ./start.sh。
2. 安装 Hugging Face 下载工具
自动下载模型需要支持传输功能的 Hugging Face CLI:
pip install -U 'huggingface_hub[hf_transfer]'
下载支持断点续传。如果中断的运行导致模型不完整,请再次运行启动器或下载器。
3. 可选择在不启动服务器的情况下下载
如需在启动 vLLM 前准备 EXL3 和 Engram 文件,请运行:
./download.sh
默认启用自动下载。如果希望自行管理文件,请在配置中设置 AUTO_DOWNLOAD=0。
启动服务器
在头节点的仓库目录中运行:
./start.sh
首次运行时,启动器会执行以下高级操作:
- 检查可用内存余量和节点配置。
- 下载缺失的 EXL3 和 Engram 文件。
- 拉取公共镜像
ghcr.io/miaai-lab/deepseek-v4.1-flash-exl3-2x-dgx-sparks:2.9bpw。 - 仅当配方标记发生变化或注册表不可用时才在本地构建。
- 使工作节点可以访问权重。
- 运行 EXL3 GEMM 和 Engram 反量化 GPU 检查。
- 启动两个张量并行 rank,并在端口
8888上公开 API。
干净的检出版本应与已发布镜像中的配方标记一致,因此通常无需编译。
控制镜像构建和传输
BUILD=1 ./start.sh始终在本地构建镜像。SKIP_BUILD=1 ./start.sh即使拉取的镜像与其配方标记不同,也会保留该镜像。SKIP_PULL=1 ./start.sh避免联系容器注册表。PULL=1 ./start.sh即使本地标记匹配,也会再次拉取镜像。SKIP_SHIP=1 ./start.sh阻止 head 将镜像复制到 worker。
默认情况下,镜像传输使用通过可续传 rsync 传输的分阶段 tar 文件。设置 IMAGE_SHIP=stream 可将 docker save 的输出通过管道传给 worker 的 docker load。流式传输不需要临时空间,但中断后必须从头开始传输。auto 模式优先使用可续传传输,并会在 worker 磁盘空间有限时回退到流式传输。
检查和管理部署
请使用启动器的管理命令,而不是分别操作两个容器:
./start.sh status
./start.sh logs
./start.sh logs worker
./start.sh stop
./start.sh restart
如果 worker 已经同步了权重,可以在不重复同步的情况下重启:
SKIP_SYNC=1 ./start.sh restart
启动期间,持续 420 秒无日志检测器会从两个 rank 收集 Python 堆栈跟踪。诊断文件会写入 logs/。
发送第一个 API 请求
服务器运行正常后,向 head 节点发送聊天补全请求。此冒烟测试请求会禁用思考模式,以便直接检查答案:
curl -s http://127.0.0.1:8888/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"DeepSeek-v4.1-Flash-EXL3","messages":[{"role":"user","content":"What is 17*19? Reply with the integer only."}],"max_tokens":32,"temperature":0,"chat_template_kwargs":{"enable_thinking":false}}'
预期答案是 323。任何兼容 OpenAI 的客户端都可以使用相同的基础 URL 和模型标识符。
使用推荐的采样设置
对于普通模型使用,官方采样设置如下:
temperature=1.0top_p=0.95- 启用思考模式
聊天模板默认使用较高的推理强度,以 75 表示。它也接受 low(表示 50)、max(表示 100),或 1 到 100 之间的整数。
使用推荐采样配置的请求示例如下:
curl -s http://127.0.0.1:8888/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"DeepSeek-v4.1-Flash-EXL3","messages":[{"role":"user","content":"Explain how tensor parallel inference works."}],"max_tokens":512,"temperature":1.0,"top_p":0.95,"reasoning_effort":"high"}'
了解 DSpark 推测解码
该部署使用 DSpark 推测解码,草稿专家已嵌入检查点中。不需要单独下载、加载或提供草稿模型。
底层 vLLM 配置等效于:
--speculative-config '{"method":"dspark","num_speculative_tokens":3}'
该检查点的草稿块大小上限为五,但部署设置使用三个推测 token,因为测量结果显示其在散文文本上更快。捕获大小包括 1 2 3 4 6 8 12 18 24,其中包括大小六,用于两个序列各生成三个推测 token 的情况。
在报告的测量结果中,DSpark 提升了单流解码性能。对于更大的批次,可使用 SPEC_METHOD=none 禁用它。这样还可释放约 3.5 GiB。README 报告单个推测流约为每秒 31.6 个 token,而在四个流的情况下,禁用推测的服务达到了更高的总吞吐量。
安全管理内存
每个节点的大致内存分配包括:每个 rank 使用 99.5 GiB 的 EXL3 权重、2.5 GiB 的 KV 池、上下文、NCCL、工作区和 CUDA 图占用的大约 5 至 7 GiB,以及 vLLM 进程、Docker、操作系统和桌面占用的约 9 GiB。
部署提供的限制如下:
MAX_MODEL_LEN=600000MAX_NUM_SEQS=2MAX_NUM_BATCHED_TOKENS=1024- 2.5 GiB 的 KV 缓存池
KV 布局会自动选择为 DeepSeek 的 fp8_ds_mla。不要传递 --kv-cache-dtype。测得的内存池在上下文限制为 614,400 时可容纳 774,400 个 token,每个 token 的成本约为 3.4 KiB。
不要通过将 2.9 转换为整数 2 来估算此检查点的量化方式。其量化参数按张量选择,不同的路由专家、共享专家、注意力矩阵、Engram 张量、MTP 张量和语言模型头使用不同的值。
预热后,在报告的测试中,head 保留了约 4.07 至 4.21 GiB 的可用内存。一次 601,000-token 的预填充会将余量降至约 2.1 GiB。因此,请在长时间预填充后验证更改,而不是刚启动后立即验证。
一次只增加一个与内存相关的设置。README 警告,将 KV 池增加 0.5 GiB 可能会损失约 1 GiB 的预填充余量。3 GiB 的内存池通过了短暂的冒烟测试,但在 600,000-token 提示词中运行到约 470,000 个 token 时失败。
谨慎使用可选的内存保护
scripts/memguard.sh 每秒检查一次 /proc/meminfo,并在连续两个采样低于 DSV41_MEM_GUARD_GIB 后终止本地服务容器。该变量的默认阈值为 1.5 GiB。
该保护机制默认处于禁用状态,因为它无法确定是哪一个进程消耗了内存。它始终会终止自身的 vLLM 容器,即使造成内存压力的是主机上的无关工作负载。若要显式启用,请设置:
DSV41_MEM_GUARD=1
对于占用大量资源的主机端任务,README 建议改为限制造成问题的任务:
systemd-run --scope -p MemoryMax=2G <job>
该部署仍会为两个容器使用 --oom-score-adj 1000,并结合启动余量预检、每次预填充后的分配器释放,以及加载完成后的页缓存清理。
选择权重同步策略
NFS:默认策略
NFS 是与经过测试的硬件配置相匹配的方案。它复用 vllm-fn-nfs 导出器,公开 EXL3 和 slim Engram 目录,并将它们挂载到工作容器中的 /model 和 /engram-src。
./start.sh share
./start.sh
start.sh 会自动执行共享步骤,因此显式运行 share 命令主要适用于准备或排查导出配置。
ZFS:节点本地副本
在 .env 中设置 WEIGHT_SYNC=zfs,即可在每个节点上维护本地数据集。ZFS 可以提高容错能力,并在初始副本完成后仅发送发生变化的块,还支持基于快照的回滚。
代价是空间需求很高:每个节点大约需要 385 GiB 的可用存储池空间。README 指出,经过测试的第二个 Spark 仅剩约 41 GiB 可用空间,因此无法在那里使用 ZFS。
每个节点上的典型配置如下:
sudo apt-get install -y zfsutils-linux
sudo zpool create -f models <vdev>
sudo zfs create -o recordsize=1M -o atime=off -o compression=lz4 \
-o mountpoint=/path/to/model models/dsv41-exl3
sudo zfs create -o recordsize=1M -o atime=off -o compression=lz4 \
-o mountpoint=/path/to/engram models/dsv41-engram
sudo zfs allow -u "$WORKER_USER" create,destroy,mount,receive,snapshot,rollback models
仅使用分片 47 和 48 以及索引填充 Engram 数据集。不要复制完整的 476 GiB 原生模型目录树。如果存储池或工作进程的 zfs recv 不可用,启动器会回退到流式传输。
Rsync:更简单的本地副本
WEIGHT_SYNC=rsync 会创建传统的节点本地副本,无需 ZFS 存储池。它不提供 ZFS 的块级增量更新。
打包 Engram 数据以加快预填充
Engram 表仍采用原生 FP8 格式,不属于量化后的 EXL3 目录树。默认情况下,基于文件的嵌入实现会检索各行,而不会将每层约 47 GiB 的数据固定在内存中。
要将特定秩的 Engram 行文件移动到每个节点的本地 NVMe,请运行:
./start.sh pack
在张量并行大小为二时,打包会在每个节点写入约 94 GiB 数据。该步骤是可选的,并非启动所必需。在报告的测试中,本地打包的 Engram 文件将预填充性能提升了约 25% 至 50%,因为行缺失时使用的是本地直接 I/O,而不是 NFS。
将 Engram 行传递给反量化内核时,必须保持其类型为 fp8_e4m3fn。将其视为 uint8 可能会产生看似合理但不正确的输出。
运行验证测试
该仓库包含无需 PyTorch 或 vLLM 的源代码级检查:
python3 tests/test_numeric_config.py
python3 tests/test_engram_src.py
python3 tests/test_engram_secondary.py
python3 tests/test_k_map.py
python3 tests/test_memory_log.py
python3 tests/test_exl3_lm_head.py
python3 tests/test_sm120_block64.py
python3 tests/test_h2d_stage.py
python3 scripts/weight_budget.py --tp 2
聊天模板一致性测试需要 transformers 和原始模型的 encoding/encoding.py:
python3 tests/test_chat_template.py --src /path/to/DeepSeek-V4.1-Flash
API 运行后,执行端到端冒烟测试:
bash tests/test_smoke.sh
冒烟测试会要求模型计算 17*19,并期望得到 323。启动服务前,start.sh 还会在临时容器中运行 GPU 一致性和 Engram 反量化检查。结果会写入 logs/overlay-verify.log。
高级运维提示
- 一到两个流时优先使用 DSpark:报告的单流解码速率为每秒 31.6 个 token,双流聚合速率为每秒 42.5 个 token。
- 更大批次时禁用推测:设置
SPEC_METHOD=none后,单流吞吐量下降,但在报告的测试中,四流聚合吞吐量达到每秒 53.7 个 token。 - 重复使用较长提示词时打包 Engram:本地 NVMe 行文件可减少对头节点 NFS 导出的依赖,并能显著提升预填充速度。
- 保持部署为纯文本模式:SM120 内核环境不提供模型视觉 token 宽度所需的稀疏 MLA 内核。
- 不要随意重新启用 EXL3 辅助流:部署会将 EXL3 流串行化,因为不同 CUDA 流上的两个内核可能因运行时在每个设备上仅使用一个锁缓冲区而发生死锁。
- 监控正确的网络接口:NCCL 无法使用
10.0.0.x回环别名。每个 NIC 的 GID 选择不正确时,可能在约一分钟后因ibv_modify_qp错误码 61 失败。 - 检查长预填充时的内存:成功启动和短时冒烟测试并不能证明更大的 KV 池或上下文设置是安全的。
- 使用仓库启动器:它负责协调 NFS 或本地同步、预检、镜像验证、双 rank 启动和诊断。
预期性能
已发布的测量结果显示,单解码流速率为每秒 31.6 个 token,双流聚合速率为每秒 42.5 个 token。使用打包的 Engram 数据和分组专家内核时,短提示词预填充速度约为每秒 965 至 1,041 个 token。
长提示词预填充速度自然会降低。报告示例包括:100,000 个 token 的提示词约为每秒 979 个 token,455,000 个 token 时为每秒 802 个 token,601,000 个 token 时为每秒 810 个 token。实际结果取决于提示词形态、存储配置、推测设置、批次宽度以及配置的预填充分块大小。
结论
本项目整合了在两台 GB10 DGX Sparks 上运行 DeepSeek V4.1 Flash 服务所需的专用内核、EXL3 加载器、文件后备 Engram 实现、网络配置、内存控制和启动器。请先使用随附的默认设置,通过冒烟测试验证 API,然后一次调整一个设置。为确保运行安全,应在真实的长预填充工作负载下同时评估吞吐量和可用内存。
