mcpField
S
spark-training-gotchas

spark-training-gotchas

github.com/wshobson云与 DevOps41.9k2.7k 次安装2 周前更新
代码仓库

关于 spark-training-gotchas

面向 DGX Spark 训练运维的故障排查技能,覆盖启动、内存、性能和精度问题。它将十类常见故障编号为 G1–G10,并提供检查方法及预检脚本。

主要功能

  1. 01十类编号故障
  2. 02症状、原因、检查与修复说明
  3. 03针对部分问题的预检脚本
  4. 04双 Spark 并行策略建议

适用场景

  1. 01排查 CUDA 导入错误或段错误
  2. 02调查仍显示有余量时发生的 OOM
  3. 03检查长时间运行中的吞吐下降
  4. 04为双 Spark 训练选择并行策略

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshobson/agents --skill spark-training-gotchas

安装后在 Claude Code 中输入/spark-training-gotchas确认已加载。

附带文件assets/preflight.shreferences/gotcha-checks.md

详细介绍

Spark 训练避坑指南

DGX Spark 的 GB10 芯片(Grace Blackwell、SM121、128GB 统一 内存、aarch64)在启动、内存、散热、带宽和精度方面有十种反复 出现的故障模式。每一种都被命名为 G1–G10,以便按编号检查—— 这套编号对运行这些检查的工具至关重要。请在长时间运行之前 阅读本文,而不是在第六个小时之后。

何时使用本技能

  • 训练运行无法启动,出现导入错误或段错误,但并未指向 真正的原因。
  • 运行发生 OOM,而 nvidia-smi 仍显示有余量。
  • 一个起初正常的运行在中途吞吐量下降。
  • 在 GB10 上执行任何数小时或多轮次的任务之前。
  • 将两台 Spark 连接在一起时,在选择并行策略之前。
  • 为 Spark 托管的运行在 FP8 和 NVFP4 之间做选择时。

常见问题速查

#症状修复
G1undefined symbol / 段错误cu130 wheel 或容器
G2flash-attn 使用了错误的后端跳过 pip 构建;在 NGC 上 monkeypatch
G3有余量却 OOM丢弃页缓存
G4吞吐量下降 / 重启预期约 100W 持续上限
G5内存受限的步骤缓慢按 180–192 GB/s 预算
G6缓存运行中途被逐出一次只运行一个 GPU 服务
G7NVFP4 比 FP8 慢除非 sm_121a,否则保持 FP8
G8playbook 直接失败检查上游 issue
G9安装后环境损坏使用容器
G102-Spark TP 挂起仅用 DDP/FSDP,绝不用 TP

十大坑

G1:CUDA 12/13 ABI 不匹配
  • 症状: ImportError: undefined symbol 并指出某个 CUDA 函数,或在第一次 .cuda() 调用时段错误。
  • 原因: 大多数 PyPI wheel 链接 libcudart.so.12;Spark 搭载 CUDA 13。pip 从不检查 CUDA ABI,因此只在导入或首次 内核启动时才会暴露。
  • 检查: references/gotcha-checks.md G1 —— wheel 的 CUDA 构建标签。
  • 修复: 从 download.pytorch.org/whl/cu130 重新安装,或 使用匹配的容器。
G2:flash-attn —— 跳过 pip 构建,留意 Unsloth 的自动检测
  • 症状: pip install flash-attn 仍然失败/挂起。 Unsloth 还可能在没有明确请求的情况下静默地在 SDPA 上 训练 flash-attn。
  • 原因: 裸 pip 没有 aarch64/sm_121 wheel —— 但 NGC 容器附带可用的 SM121 flash-attn,且 Unsloth 会自动优先 使用它,从而丢弃 attn_implementation="sdpa"。
  • 检查: references/gotcha-checks.md G2 —— flash-attn 是否已存在且可用。
  • 修复: 裸 pip —— 跳过 flash-attn,使用 SDPA(保持不变)。 在 NGC 上 —— 唯一可靠的覆盖方式是 references/gotcha-checks.md G2 中的 monkeypatch。
G3:低于 128GB 的 UMA OOM
  • 症状: 在模型加载/训练期间发生 OOM,而 nvidia-smi 仍报告低于 128GB 上限的可用内存 —— 或者,在某些设置下,直接显示 [N/A] 而不是数字。
  • 原因: 在 safetensors 加载期间,mmap 和 CUDA 分配器会 重复计算页;QLoRA 可能比 bf16 更早 OOM,因为反量化会 增加临时分配。
  • 检查: references/gotcha-checks.md G3 —— 读取 free -g 和 /proc/meminfo,而不是 nvidia-smi。
  • 修复: 用 sync; echo 3 > /proc/sys/vm/drop_caches 丢弃页缓存 —— 需要 root,是运行之间的重置,而不是训练中途的步骤。
G4:热节流
  • 症状: 吞吐量在数小时的运行中途下降,或机器在持续 负载下自发重启。
  • 原因: 持续功耗上限约为 100W,而额定值为 240W;长时间运行会逼近该上限并节流,有时还会重启。
  • 检查: references/gotcha-checks.md G4 —— 采样 nvidia-smi --query-gpu=temperature.gpu,power.draw。
  • 修复: 如果功耗在 240W 以下趋于平稳而温度 攀升,则将节流视为原因;改善散热或 限制运行时长。
G5:带宽上限
  • 症状: 内存受限型工作负载,尤其是解码密集的 RL 循环,吞吐量远低于预期便进入平台期。
  • 原因: 273 GB/s 是规格上限,并非持续带宽;实测带宽在 180–192 GB/s 之间。
  • 检查: references/gotcha-checks.md G5 — 将观察到的步长时间与实测区间对比,而非规格值。
  • 修复: 按 180–192 GB/s 来规划吞吐量;修订基于 273 GB/s 数值制定的方案。
G6:全局 UMA 资源争用
  • 症状: 某个进程的 KV cache/权重在运行中途被静默逐出,其自身日志中没有 OOM。
  • 原因: 统一内存是一个全局池;一个未设上限或接近容量的进程会与其他任何进程竞争,并可能将其逐出。一个小规模、有上限的工作负载则不会——一个 <4GB 的 LoRA 可与上限设为 gpu-memory-utilization<=0.5 的 vLLM 共存无碍。
  • 检查: references/gotcha-checks.md G6 — 其他驻留 GPU 的进程以及它们是否设了上限。
  • 修复: “单一重负载作业”规则适用于未设上限或接近容量的工作负载——先给无关服务设上限或将其停止。一个小规模、有上限的工作负载则无需停止。
G7:在 SM121 上 NVFP4 比 FP8 更慢
  • 症状: 在 Spark 上将推理工作负载从 FP8 切换到 NVFP4 后反而更慢,而不是更快。
  • 原因: 除非内核以 sm_121a 为目标,SM121 缺少 cvt.e2m1x2;缺少它时 NVFP4 运行速度约慢 32%。
  • 检查: references/gotcha-checks.md G7 — 能力报告为 (12, 1);构建是否以 sm_121a 为目标?
  • 修复: 除非构建以 sm_121a 为目标,否则继续使用 FP8。
G8:过时的官方 Playbook
  • 症状: 按照官方 DGX Spark playbook 操作仍然失败,且没有本地配置错误可以解释。
  • 原因: 官方 playbook 以前就发布过有问题的版本;技术栈的演进快于文档。
  • 检查: references/gotcha-checks.md G8 — 该 playbook 仓库近期的 issue。
  • 修复: 在信任某个配方进行高昂成本的运行之前,先查看 github.com/NVIDIA/dgx-spark-playbooks 的 issue。
G9:容器优先,而非裸 pip
  • 症状: 昨天还能用的裸 pip 环境在一次无关的 pip install 之后坏了,或者两个“完全相同”的环境表现不同。
  • 原因: 裸 pip 让 Triton、xformers 和 transformers 各自漂移;没有任何东西将它们固定到 GB10 的 SM121 目标上。
  • 检查: references/gotcha-checks.md G9 — 容器还是裸 pip?
  • 修复: 优先使用 NGC 容器(标签指引见 spark-environment-setup)或 Unsloth 的容器。如果无法避免裸 pip,请遵循 NVIDIA 的安装顺序,包括对 Unsloth 使用 --no-deps。
G10:双 Spark 仅支持 DDP/FSDP
  • 症状: 跨两台 Spark 的张量并行启动会挂起、比单台 Spark 慢得多,或报错。
  • 原因: ConnectX-7 的速度足以进行梯度/参数同步(DDP、FSDP),但对于 TP 的细粒度流量来说过于单薄。
  • 检查: references/gotcha-checks.md G10 — 所配置的并行策略。
  • 修复: 在两台 Spark 的配置中,选择 DDP 或 FSDP,绝不要用张量并行——这里 TP 仅限单节点。

快速排查

在其他操作之前最先要运行的最廉价检查:

python3 -c "import torch; print(torch.version.cuda)"  # expect 13.x (G1); NGC builds have no +cu130 tag — that's not a failure
import torch; print(torch.cuda.get_device_capability())  # expect (12, 1) (G7)
{ [ -f /.dockerenv -o -f /run/.containerenv ] || grep -qE 'docker|containerd' /proc/1/cgroup; } 2>/dev/null && echo container || echo unknown  # G9

assets/preflight.sh 会运行 G1、G3、G4、G7、G9,并以固定格式为每个坑输出一行:先是 G 编号,然后是 PASS/FAIL/WARN(可自动化的部分)、不可用时的 SKIP,或用于原始读数的 INFO:(G3、G4)。完整命令见:references/gotcha-checks.md。假定可用的环境另见 spark-environment-setup。

评论

支持 Markdown