- 人工智能
- 大模型
- AI 技能/插件
- 分布式训练
- 微调
- 深度学习
【免费下载链接】ml-engineering
Machine Learning Engineering Open Book
CPU 内存(RAM)往往是 ML 训练集群规划中最容易被忽视的一环:GPU 显存有明确的规格书,而节点内存的"够用"标准却缺乏量化依据。本篇指南基于 ml-engineering 仓库的 compute/cpu-memory/README.md 章节,系统讲解 CPU 内存的容量下限经验法则、六大典型用途(权重加载/保存、参数与激活 offload、DataLoader 预取、软件运行时开销),并深入剖析 DataLoader 使用 HFdatasetsmmap 模式时出现的"Resident 内存异常占用"误报问题。读完你将能够为自己的 8 卡节点制定合理的内存采购与分配方案,并能正确解读top/free中的内存指标,避免被 mmap 映射产生的假象误导。
CPU 内存:越少人需要操心的部分越好
ml-engineering 将 CPU 内存章节定位为全书最短的章节之一(见 compute/README.md 的描述:"how much CPU memory is enough - the shortest chapter ever"),因为相对于 GPU 显存,CPU 内存的调优空间很小——这本身是件好事。绝大多数 ML 计算发生在 GPU 上,CPU 内存主要扮演"数据中转站"和"后备仓库"的角色。
这一"简短"建立在一条清晰的经验法则之上:
每个节点的 CPU 内存至少应与该节点的 GPU 显存总量持平。
例如,一个 8× 80GiB H100 节点拥有 640GiB GPU 显存,那么该节点的 CPU 内存就至少应为 640GiB。主流云厂商的高端机型通常标配 1–2TiB CPU 内存,完全可以覆盖这一需求。这条规则确保了任何需要先在 CPU 侧落脚、再流向 GPU 的数据都有足够的周转空间。
ML 工作负载中 CPU 内存的六大用途
虽然 GPU 承担了绝大部分计算,但以下六类操作都离不开 CPU 内存,且多数是**瞬时(transitory)**使用——用完即归还。
1. 加载模型权重(瞬时占用)
模型权重在启动时从磁盘读入 CPU 内存,然后被搬运到 GPU。这一阶段的内存占用是瞬时的:一旦所有权重转移到 GPU,CPU 侧占用便归零。对于动辄数十 GB 的模型,若节点 CPU 内存不足,加载阶段就可能触发 OOM 或严重的 swap。
2. 保存模型权重(瞬时占用)
保存检查点时存在两种路径:
- 各 GPU 直接写盘:每个 GPU 进程把自己持有的分片直接写入磁盘,CPU 侧几乎不额外占用;
- 先在 CPU 侧重组再写盘:模型被在 CPU 内存中重组成完整形态后再写盘,需要临时容纳整个模型的 CPU 内存。
无论哪种方式,这同样是瞬时占用,但规划容量时仍需保证峰值需求能被满足,尤其是第二种路径。
3. 参数与优化器状态 offload(可能的大额需求)
使用 DeepSpeed,其中还指出:DeepSpeed 一类的框架还能把部分计算工作 offload 到 CPU 而不形成瓶颈,这种情况下额外多核 CPU 也有收益。
4. 激活值 offload
forward过程中计算的、且backward阶段仍需使用的激活值,除了丢弃后在反向时重算(gradient checkpointing 的路线),也可以被 offload 到 CPU 内存暂存。这避免了重算带来的额外开销,代价是占用 CPU 内存并增加 CPU↔GPU 的传输量。这一用途在显存紧张的大模型训练中常与梯度检查点方案交替使用,需要根据传输带宽与重算代价权衡。
5. DataLoader:通常是最大的 CPU 内存消耗者
DataLoader 是 CPU 内存最主要、最不稳定的占用来源。默认配置num_workers=0是同步取数,但异步取数(num_workers>0)才是消除 IO 与数据预处理对加速器阻塞的关键(详见 training/performance/README.md 的 "Asynchronous DataLoader" 一节)。
典型节点上每块加速器配 2 个 worker,即每个 8 卡节点至少运行 2×8=16 个 DataLoader worker 进程,每个进程都持有若干数据样本。当数据以流式方式从云端拉取且数据分片(shard)很大时,这 16 个进程轻松就能吃掉数百 GB CPU 内存——IDEFICS-80B 训练时的实测是约 1TiB CPU 内存仍不足以支持足够的 worker 数量,导致 DataLoader 成为训练瓶颈(training/performance/README.md 中的 case study)。
CPU 核数规划也与 DataLoader 直接相关:按 compute/cpu/README.md 的公式,每块加速器需要 1 个进程核 + 每个 worker 1 个核,8 卡节点总计约8*(num_workers+1)+4个核(NLP 场景num_workers=2约需 28 核,CV/VLM 场景num_workers=4约需 44 核)。worker 数越多,内存与核数双重需求越大。
6. 软件与依赖库本身(通常可忽略)
Python 运行时、框架及其依赖库也会占用少量 CPU 内存,但相比上述用途通常微不足道,无需特别规划。
必须知道的一件事:mmap 模式下 Resident 内存的巨大误报
若 DataLoader 使用 HF
datasets的 mmap 模式,top中显示的 Resident 内存(RSS)可能高得吓人——它会把整个数据集都"映射"到地址空间里。但这是一种误导:当其他程序需要内存时,OS 会把未被引用的 mmap 页换出(page out)还给系统。
这一机制值得深入理解,因为它同时解释了两个问题:为什么 mmap 是 HFdatasets的默认路径,以及为什么你看到的高 RSS 不是内存泄漏。
mmap 为什么是默认路径:Arrow 的按需分页读取
HFdatasets把数据保存在 Apache Arrow 文件中——一种二进制列式格式,磁盘上的字节布局与内存中使用的布局一致,因此读取某一行无需任何解析步骤。这让内存映射成为默认方式(load_from_disk,或load_dataset不指定keep_in_memory):文件被映射进地址空间,行访问退化为一次缺页(page fault)而不是一次read系统调用。仓库中的 storage/README.md 对此有完整的技术剖析,并附有基准数据。
假象的真相:RSS 高 ≠ 内存泄漏
映射所显示的 Resident 内存并非泄漏——OS 可以回收未被引用的页。当内存紧张时,未使用的 mmap 页会被自动换出,因此系统整体内存不会被无限消耗。这一认知不仅适用于 HFdatasets,任何使用mmap的数据集都遵循同样的行为(compute/cpu-memory/README.md)。
mmap 与顺序读取的实测差距:何时需要放弃 mmap
mmap 并非免费的。仓库中的 storage/README.md 记录了用 mmap-io-bench.py 测得的实测数据(1GiB 文本、32768 行、每行 32KiB、三个 Arrow 分片,每次运行前用posix_fadvise(POSIX_FADV_DONTNEED)丢弃页缓存):
| 1GiB 的读取方式 | 本地 NVMe | 相对顺序读的降速 | Lustre | 相对顺序读的降速 |
|---|---|---|---|---|
顺序read()整文件 | 0.14s | 0% | 1.87s | 0% |
| mmap,按行顺序遍历 | 0.53s | 279% | 3.11s | 66% |
| mmap,随机打乱行序 | 0.78s | 457% | 94.2s | 4_937% |
结论非常明确(storage/README.md):
- 按序消费分片(校验、格式转换、单遍 tokenize):直接用普通
read()大块读取,不要映射; for row in ds遍历映射数据集:本地 NVMe 上映射代价 279% 但仍半秒读完 1GiB,可接受;在 Lustre 上每页都要走网络,训练前应先把分片拷到节点本地盘;- 随机打乱
ds[i](常规 shuffled 训练):在 Lustre 上每 GiB 耗时 94.2s,是顺序读的约 50 倍,绝不可直连网络文件系统。解决方案包括:把分片暂存到节点本地 NVMe 再映射、预打乱成 epoch 分片后按序读取、或流式加缓冲内打乱;keep_in_memory=True也能消除缺页,但代价是每个 DataLoader worker 都要重复承担这份 RAM。
内存观测与排障
- 查看每个进程真实的物理内存占用,可用
top/htop的 RES 列,但要结合上述 mmap 机制解读; - 仓库的 debug/README.md 提供了 GPU 内存分配检测脚本(如
torch-distributed-gpu-test.py检查集群 GPU 间通信与显存分配能力),排查思路同样适用于 CPU 侧内存观测; - 若怀疑 DataLoader 内存异常,优先检查:worker 数量、数据集是否 mmap 模式、分片大小、是否流式拉取、是否使用 pinned memory(
pin_memory=True会阻止对应页被换出,减少可用内存总量,详见 training/performance/README.md)。
实操检查清单:为你的训练节点规划 CPU 内存
- 算下限:
节点 CPU 内存 ≥ 节点 GPU 显存总量(如 8×80GiB → ≥640GiB); - 加 DataLoader 余量:按
num_workers数量估算峰值占用,流式云端数据场景留足数百 GB 冗余; - 加 offload 余量:若启用 DeepSpeed ZeRO-Offload 或激活值 offload,按模型与优化器状态规模单独估算;
- 理解 mmap:看到 RSS 虚高先别急着加内存,确认是否 mmap 数据集,确认 OS 是否在内存紧张时正常回收;
- 衡量读取模式:跑一遍 mmap-io-bench.py(需
pip install datasets numpy,脚本仅支持 Linux),把训练的数据读取路径与上表对照,决定是否改走顺序读、本地暂存或keep_in_memory。
小结
CPU 内存的规划原则可以浓缩为一句话:以 GPU 显存总量为下限,为 DataLoader 和 offload 留足余量,并对 mmap 数据集的 RSS 数值保持正确的解读。掌握这三点,就能避免两类最常见的生产事故——节点因 CPU 内存不足在启动或取数阶段 OOM,以及因误读 mmap 内存占用而盲目扩容。仓库的 compute 章节体系(CPU 核数规划见 compute/cpu/README.md,文件系统与 IO 深入分析见 storage/README.md)为本文所述规则提供了完整的上下文与可复现的实验支撑。
- 人工智能
- 大模型
- AI 技能/插件
- 分布式训练
- 微调
- 深度学习
【免费下载链接】ml-engineering
Machine Learning Engineering Open Book
相关推荐
Temporal集群资源规划:CPU、内存与存储配置指南
Temporal集群资源规划:CPU、内存与存储配置指南 Temporal作为一款强大的分布式工作流引擎,其集群的稳定运行离不开合理的资源规划。本文将详细介绍T
后端工作流自动化任务调度TDengine 资源规划与容量估算指南:内存、CPU、存储、网络与端口规划
TDengine 资源规划与容量估算指南:内存、CPU、存储、网络与端口规划 TDengine 是一个高性能、低资源占用的时间序列数据库,适用于 IoT、IT
数据库时序数据库物联网大数据实时分析云原生ML 工程中的 CPU 规划指南:核心数估算、NUMA 亲和性与超线程实战(ml-engineering 手册解读)
ML 工程中的 CPU 规划指南:核心数估算、NUMA 亲和性与超线程实战(ml engineering 手册解读) 截至 2026 年,绝大多数机器学习训练与
人工智能大模型AI 技能/插件分布式训练微调深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考