mcpField
S
spark-memory-thermal-ops

spark-memory-thermal-ops

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

关于 spark-memory-thermal-ops

面向 DGX Spark GB10 训练用户的技能,指导统一内存预算、OOM 排查和长任务热状态监控,并说明并发负载的资源竞争。

主要功能

  1. 01按 free -g 规划内存余量
  2. 02按顺序处理统一内存 OOM
  3. 03记录温度与功耗变化
  4. 04评估训练与推理并发

适用场景

  1. 01启动前估算训练任务是否适配内存
  2. 02训练中 OOM 时逐步采取缓解措施
  3. 03长时间任务降速时检查热节流
  4. 04决定是否停止未限额的推理服务

安装

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

$npx skills add https://github.com/wshobson/agents --skill spark-memory-thermal-ops

安装后在 Claude Code 中输入/spark-memory-thermal-ops确认已加载。

附带文件assets/thermal-sample.shreferences/uma-accounting.md

详细介绍

Spark 内存与散热运维

DGX Spark 的 GB10 芯片拥有一个 128GB 的统一内存 (UMA) 池,由 CPU 和 GPU 共享,并且其持续功率上限远低于额定值。这两点都打破了独立 GPU 的假设:余量并非 nvidia-smi 所报告的那样,而且一次开始很快的运行会在任务中途变慢,却没有任何配置错误。本技能涵盖内存余量的规划、处理实际的 OOM,以及在长时间任务中监控散热。关于启动时的故障模式(ABI 不匹配、flash-attn、playbook 损坏),请参阅 spark-training-gotchas —— 本技能假设任务能够启动。

常见问题快速参考

情况做法
启动前规划余量依据 free -g 做预算,而非 nvidia-smi —— 参见 UMA 内存模型
任务在统一内存上 OOM按顺序执行 OOM 阶梯:先 flush,再调整 batch/pack,最后降级方法
运行中途吞吐量下降在假定是配置问题之前先检查功率/温度日志 —— 参见散热监控
同时需要训练器和推理服务一次只运行一个 —— 参见并发工作负载

何时使用本技能

  • 在启动前针对 128GB 池为一次训练运行做规模评估 —— 这个模型、方法和 batch/pack 组合是否能装下。
  • 一次运行在加载中途或步骤中途 OOM,而补救顺序很重要 —— 首先、其次、第三该尝试什么。
  • 在多小时的作业中监控温度和功率,判断变慢是散热降频还是其他原因。
  • 计划在同一台机器上让训练器与推理服务(vLLM、Ollama)并行运行。

UMA 内存模型

Spark 没有独立的 GPU 显存 —— GPU 和 CPU 共享一个 128GB 池。两个后果:

  • nvidia-smi 和 cudaMemGetInfo 会低报压力 —— 或者根本不报告任何内容。 两者报告的都是 CUDA 分配器可见的内存,而非池的实际状态 —— 一台机器可能在 nvidia-smi 中显示有余量,却仍然 OOM,因为分配器看不到的页缓存和 mmap 页面消耗着同一个池。在某些驱动/配置下,内存查询会直接返回 [N/A], [N/A] 而不是数字 —— 在那里用 grep 查找数值的脚本会一无所获,而不是得到一个误导性的低估值(参见 spark-training-gotchas 的 gotcha G3)。

  • 模型加载是一个瞬时峰值,而非稳态。 加载 safetensors 权重时会 mmap 该文件,然后复制到 CUDA 张量中 —— 在加载期间的一个窗口内,mmap 页面和 CUDA 副本会同时计入池。一个在训练时能装下的模型,如果在加载期间仍可能 OOM,是因为余量是按加载后的占用而非这个翻倍的瞬态来设定的。

用 free -g 而非 nvidia-smi 来规划和诊断:

free -g | awk 'NR==2 {print "free:", $4, "GB"}'

经验法则:取那个 free 值,减去几 GB 用于 OS/驱动开销,然后依据结果做预算 —— 而不是 128GB 的规格数字。 references/uma-accounting.md 中的工作表接受参数量、dtype 和方法作为输入,并返回一个内存估算值,用于与已知锚点进行比较。

规划顺序

启动前,按顺序完成以下步骤:

  1. 读取 free -g;减去 OS/驱动开销作为预算。
  2. 根据 references/uma-accounting.md 估算权重 + 优化器 + 梯度 + 激活值。
  3. 与最接近的锚点(70B QLoRA、27B LoRA、9B 全量 FT)比较,而不是仅凭估算值。
  4. 如果估算值接近预算,就从更短的 packing 或更小的 batch 开始 —— 这比在运行中途触及 OOM 阶梯代价更低。
示例:为 70B QLoRA 运行做规模评估

对照 ≈40GB 锚点对工作表公式做一次合理性检查:

params = 70e9
weights_gb = params * 0.5 / 1e9      # NF4, step 1
adapter_gb = 0.5                     # step 5, negligible
total_gb = weights_gb + adapter_gb   # + activations
print(f"{total_gb:.0f}GB before activations")

仅权重就接近 ≈40GB 锚点 —— 对同一模型类别估算远高于此的计划,是一个需要重新检查 dtype 和方法的信号。

OOM 阶梯

当作业在统一内存上发生 OOM 时,请按顺序执行这套阶梯。每一步都比上一步更具破坏性——不要跳步: 减小 batch size 永远不是第 1 步。

  1. 清空缓冲区缓存。 上一次运行或读取大型数据集留下的页缓存,往往就占用了数 GB 的“缺失”余量。这一步除了重跑之外没有任何代价,也不会改动作业的配置:

    sync; echo 3 > /proc/sys/vm/drop_caches
    

    需要 root 权限;这是运行之间的重置,而不是训练中途的步骤。关于这一步背后的完整诊断,参见 spark-training-gotchas(gotcha G3)。

  2. 减小 batch size 或 packing length。 只有在清空缓存仍未能释放出足够余量之后,才削减 batch size 或 packing length——这是第一个会改变运行行为的步骤。优先调整 packing length;在长上下文场景下,它更直接地决定激活内存占用。

  3. 降级方法:先 bf16 LoRA,再 QLoRA。 如果清空缓存并缩小 batch/pack 后仍然 OOM,就把方法降一档——下一步是 bf16 LoRA,而不是反过来。QLoRA 的 bitsandbytes 反量化缓冲区是临时的 CUDA 侧分配,可能在等效的 bf16 LoRA 运行尚未 OOM 之前就先 OOM,尽管 QLoRA 的稳态内存占用更小。QLoRA 出现 OOM 并不能证明模型放不下。

只有在以上三步全部尝试后作业仍然放不下时,才进一步退让(更小的模型、多台 Spark)。

热监控

长时间运行会触及 Spark 的持续功耗上限,远低于标称值——这是平台预期行为,不是需要解释掉的异常症状:

  • 将温度与功耗采样与训练日志并行记录,而不是等到发现变慢之后才记录——每 30-60 秒采样一次,就能把吞吐下降与热事件关联起来。保持 assets/thermal-sample.sh 写入的 CSV 输出格式,这样时间戳就能与日志对齐:

    bash assets/thermal-sample.sh 30 thermal.log
    
  • 持续约 100W 的功耗是平台上限,不是配置 bug。 不要为了“修复”一个在负载下正常表现的平台所产生的平台期而重新调整 batch size 或精度。如果温度上升而功耗在标称 240W 以下保持平稳,这就是需要识别的特征。

  • 明确记录节流事件,而不是让运行在无记录的情况下悄悄变慢。一次运行如果每步耗时在两小时后翻倍,就应该在日志中体现出来,并与该时间戳的热采样相关联。完整的节流诊断:spark-training-gotchas(gotcha G4)。

并发工作负载

由于 128GB 内存池是全局共享的,淘汰会发生在两个进程的日志都不显示 OOM 的情况下:

  • 单重负载作业规则适用于未设上限或接近容量的工作负载——未设上限的训练器和推理服务(vLLM、Ollama)会争抢同一个内存池。一个规模小、设有上限的工作负载则不会:一个 <4GB 的 LoRA 微调可以与上限设为 gpu-memory-utilization<=0.5 的 vLLM 良好共存——在停止另一个进程之前,要检查它的上限,而不只是它是否存在。

  • 在未设上限/接近容量的争抢下,推理服务会悄悄淘汰训练器的页面,反之亦然——两者都不会记录错误,因此运行变慢或 KV cache 丢失都是需要排查的争抢症状。在长时间运行或占满内存池的运行之前,先停止无关的未设上限服务。

首先检查驻留 GPU 的进程:

ps aux | grep -E 'vllm|ollama|trl|axolotl' | grep -v grep

本流程与 spark-training-gotchas(gotcha G3、G4、G6)互补——那项技能涵盖启动阶段的失败;本流程涵盖运行中的作业。

内存计算工作表: references/uma-accounting.md。

评论

支持 Markdown