[Bug]: runai_safetensors_weights_iterator yields tensors in nondeterministic order, breaking FP8 inference on some platforms

该问题出现在用 --load-format runai_streamer 加载 FP8/NVFP4 权重的场景,典型表现是权重流式加载阶段无报错、服务正常启动并返回 HTTP 200,但生成结果为空。优先排查 checkpoint 的 safetensors 分片是否包含空文件,以及分片加载顺序(g

快速结论:该问题出现在用 --load-format runai_streamer 加载 FP8/NVFP4 权重的场景,典型表现是权重流式加载阶段无报错、服务正常启动并返回 HTTP 200,但生成结果为空。优先排查 checkpoint 的 safetensors 分片是否包含空文件,以及分片加载顺序(glob/readdir 顺序)。

适用环境:Issue 中确认的环境包括 vLLM 0.18.1、0.18.2rc1、nightly(vllm/vllm-openai:cu130-nightly)以及 vLLM nightly 0.23.1rc1.dev968+g2c17d33f4(runai-model-streamer 0.16.0、torch 2.11.0+cu130、arm64);测试平台为 DGX Spark (GB10, arm64)、AGX Thor (arm64)、DGX H100 (x86)、GB200 NVL (arm64)、GB300 NVL (arm64);模型为 nemotron-edge-4b(FP8/NVFP4)、nemotron-super-v1.5-49b(NVFP4)、NVIDIA-Nemotron-3-Nano-4B(ModelOpt FP8/NVFP4)。

最快修复方案:暂无确认的一步修复方案。Issue 最终定位到 runai-model-streamer 的请求打包缺陷(遇到文件头部的空分片时提前判定流结束,静默丢弃后续文件),修复在 run-ai/runai-model-streamer#158。可优先尝试:确认 checkpoint 不包含空 safetensors 分片,或使用包含该修复的 runai-model-streamer 版本;同时注意 vLLM 侧分片列表按未排序的 glob.glob() 顺序传入,目录顺序会影响丢权重范围。

注意事项:早期提出的 tensor.clone() 方案虽已在 #43464 合入(v0.22.0),但按 Issue 最终结论,它并未解决本次“生成空内容”问题,因为根因是权重缺失而非缓冲区数据损坏。Issue 标题中的“nondeterministic order / FP8 推理被破坏”的早期解释已被作者修正;“统一内存平台专有”的结论也被证伪——相关性实际来自集群存储读取顺序,而非内存架构。平台、架构与 vLLM 版本的对应关系不影响最终根因判断。

问题场景

用户在 vLLM 中以 --load-format runai_streamer 加载量化模型(如 Nemotron 系列 FP8/NVFP4 权重),在 DGX Spark、AGX Thor 等 arm64 平台上启动 OpenAI 兼容服务。服务启动无异常,请求均返回 HTTP 200,引擎日志显示约 128 token/请求的正常吞吐,但返回的 completion 内容为空,生成 token 解码后为空字符串;有 Thor 上的运行在 min_tokens: 128、ignore_eos: true 下只返回 1 个输出 token。相同配置 8 次 streamer 开启的运行中 7 次失败、1 次通过,表现为非确定性。

报错原文

[Bug]: runai_safetensors_weights_iterator yields tensors in nondeterministic order, breaking FP8 inference on some platforms

# 症状(无异样堆栈,重点在静默失败):
- weight streaming completes without error
- server starts and answers every request HTTP 200
- completions contain no actual content(every generated token decodes to empty text)

# 部分失败运行在解释器退出时额外抛出:
UnicodeDecodeError
  at torch.library._clear_torch_ops_cache -> torch._C._jit_get_operation

原因分析

最终定位的根因不是数据损坏,也不是平台内存架构差异:

  • checkpoint 中存在空 safetensors 分片(固定分片导出器导致 10 个分片中 8–9 个为 0 张量、仅 40 字节的合法文件)。
  • runai-model-streamer 的请求打包逻辑把位于新内部请求头部的空分片误判为流结束,静默丢弃其后所有剩余文件(详见 run-ai/runai-model-streamer#157,修复见 #158)。
  • vLLM 在 RunaiModelStreamerLoader._prepare_weights → list_safetensors 中按原始 glob.glob() 顺序传入分片列表(未排序,而 DefaultModelLoader 会排序),因此目录 readdir 顺序决定丢权重的范围:某种顺序不丢,另一种顺序会丢 335/447 个张量(fp8)或 539/539 个张量(nvfp4)。

这解释了为何排序文件能让问题“消失”(改变了 I/O 和请求打包顺序),也说明“统一内存”相关性是误导项——其实际对应集群存储的读取顺序,与内存架构无关。

Issue 中曾提出的 tensor.clone() 只解决了 GLM-5.1-FP8 在离散 GPU(8×H200)上的缓冲区复用场景(#43163、#43464),并未解决本 Issue 的空输出问题。

环境排查

  • 确认 vLLM 版本与镜像标签(如 0.18.x、0.23.1rc1 nightly、vllm/vllm-openai:cu130-nightly)。
  • 确认 runai-model-streamer 版本(Issue 复现时用到 0.16.0)。
  • 确认 PyTorch 版本与 CUDA 构建(如 torch 2.11.0+cu130)。
  • 确认启动参数使用 --load-format runai_streamer,并用默认 load format 作对照。
  • 确认模型权重格式(FP8/NVFP4)及是否为 ModelOpt 导出。
  • 检查 checkpoint 分片:是否存在 0 张量的空 safetensors 文件,以及分片文件数量。
  • 确认平台、架构与内存类型(arm64/x86、统一内存/独立显存),用于对照但不应作为唯一判断依据。

解决步骤

  1. 开启 streamer 与默认 load format 各跑一次相同推理请求,确认差异仅出现在 runai_streamer 下、且返回内容为空。
  2. 检查 checkpoint 目录下的 safetensors 分片,定位是否存在空分片(0 张量、约 40 字节)。可优先尝试重新导出为非空分片,或剔除空分片后重试。
  3. 使用包含 run-ai/runai-model-streamer#158 修复的 streamer 版本(该修复处理了空分片导致的提前判流结束问题)。
  4. 在 vLLM 侧确认所使用版本的分片列表是否已排序;若未排序,可优先尝试在 list_safetensors 路径上对分片排序,以降低 readdir 顺序导致的丢权重概率——但这是缓解手段,不能替代 #158 的修复。
  5. 如需进一步验证根因,可对 yield 张量做 SHA-256 抽样、并与 safetensors 库直接加载的基线比对,确认是否有张量未被投递。

验证方法

在相同容器与主机上,分别用 --load-format runai_streamer 与默认 load format 运行同一组请求(可用 min_tokens: 128, max_tokens: 128, ignore_eos: true),多次重复。修复有效时应满足:streamer 开启时不再出现空 completion,返回内容与默认 load format 对照一致;对权重加载做的逐张量校验应能覆盖全部预期张量数量(fp8 与 nvfp4 均无缺失),且不再出现 UnicodeDecodeError 这类退出期异常。

参考来源

vllm-project/vllm #38991

关联:run-ai/runai-model-streamer #157、#158、vllm-project/vllm #43464(clone 修复,非本问题最终解法)

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 32664

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注