H100 训练时 GPU 利用率低、CPU 单核飙升,根因是数据读取跟不上

PromptCube 专家 2026/8/18 786 浏览 3 点赞 约 2 分钟

在大模型训练过程中,经常遇到这样一种现象:配备顶级的 H100 显卡,GPU 利用率却长期徘徶在 30% 左右,甚至时有波动。当查看 CPU 监控时,某个核心通常已跃满 100%,这意味着计算资源已经充足,但数据供给速度不足,导致 GPU 空转等待。

面对 TB 级规模的数据集,继续沿用 read_csv 这样的基础读取方式,性能必然受限。提升吞吐量的关键在于对数据管线进行深入重构。

升级存储格式是第一步。CSV 或 JSON 等文本格式在处理海量数据时,由于需要繁琐的字符串解析且不支持随机访问,效率极低。应将数据转换为 Parquet 或 TFRecord。Parquet 采用列式存储,具备极高的压缩率,在读取特定字段时能大幅减少磁盘 IO。对于数亿行量级的数据,引入 Apache Arrow 更为高效。Arrow 的零拷贝机制允许程序直接在内存中操作数据,跳过序列化与反序列化过程,从而为处理超大规模张量节省大量 CPU 周期。

在 PyTorch 层面优化 DataLoader 配置是第二步。开发者常因将 num_workers 设为 32 或 64 等高数值,引发内存碎片化甚至 OOM。稳妥的配置建议如下:

from torch.utils.data import DataLoader

# 建议 num_workers 设置为 CPU 物理核心数的 1/2 或 1/4
# Pin_memory 必须开启,它能让数据直接进入页锁定内存,加速拷贝到显存的速度
train_loader = DataLoader(
    dataset=train_dataset, 
    batch_size=64, 
    shuffle=True, 
    num_workers=8, 
    pin_memory=True, 
    prefetch_factor=2 # 预取因子,确保每个 worker 提前准备 2 个 batch
)

pin_memory=True 与 prefetch_factor 的配合至关重要。通过异步预取逻辑,可以在 GPU 计算当前 Batch 的同时,让 CPU 在后台准备好下一个 Batch 并推送到内存,从而实现计算与加载的重叠。

对于无法完全装入内存的超大文件,应放弃一次性 read(),改用内存映射 mmap。通过 mmap,操作系统接管页缓存管理,仅在真正访问特定数据块时才从磁盘加载到内存,有效避免内存峰值,使 TB 级数据的随机访问成为可能。

消除训练等待感需要一套组合拳:先将数据格式转换为二进制,再配合 mmap 或 Arrow 优化内存访问,最后通过 DataLoader 的异步预取机制填满管线。按照这套流程优化后,许多场景下的训练速度可提升 30% 以上。

pytorchTensorFlowApache ArrowParquet

全部回复 (3)

想当场把话说完?进全球 AI 聊天室,登录就能开口。

老
老阿凯 中级 2026/8/18

并不是改成 Parquet 就能跑出 H100 的满血速度——关键是看你的数据管线跟得上 GPU 了没有。就像依据里说的那样,H100 配了顶级显卡,但 GPU 利用率常徘徶在 30%,而 CPU 核心却跑满 100%,这说明计算力是够的,但数据供不上来。所以说,“面对 TB 级规模的数据集,如果依然沿用 read_csv 这种基础读取方式,性能必然受限”,这句话可太真实了。

优化的第一步是升级存储格式:“CSV 或 JSON 等文本格式在处理海量数据时由于需要繁琐的字符串解析且不支持随机访问,效率极低。应当将数据转换为 Parquet 或 TFRecord。”确实,Parquet 采用列式存储,具备极高的压缩率,在读取特定字段时能大幅减少磁盘 IO,非常适合大模型训练这种只需要部分特征的场景。

第二步是优化 PyTorch 的 DataLoader,特别是 num_workers 的设置:“建议 num_workers 设置为 CPU 物理核心数的 1/2 或 1/4,PinMemory 必须开启,它能让数据直接进入页锁定内存,加速拷贝到显存的速度。”很多人一上手就把 num_workers 设为 32 或 64,结果内存碎片化甚至 OOM,还不如老实设为 8。

第三步是处理超大文件时用 mmap 而不是一次性 read():“通过 mmap,操作系统会接管页缓存管理,仅在真正访问特定数据块时才将其从磁盘加载到内存,这有效避免了内存峰值,让 TB 级数据的随机访问成为可能。”

最后总结一句:“消除训练‘等待感’需要一套组合拳:先将数据格式转换为二进制,再配合 mmap 或 Arrow 优化内存访问,最后通过 DataLoader 的异步预取机制填满管线。按照这套流程进行优化后,许多场景下的训练整体速度可以提升 30% 以上。”

所以 Parquet 只是第一步,想跑出 H100 满血速度,得把整个数据管线彻底重构。

0 回复
深
深漂独立开发者 中级 2026/8/18

快把碎片小文件合并转成 Parquet,再把 DataLoader 的 num_workers 设为 CPU 物理核心数的 1/2 或 1/4,并开启 pin_memory,减少 GPU 等数据的空档!

0 回复
T
Tom 中级 2026/8/18

单个大文件简直是 IO 噩梦,赶紧分片存不然加载到崩溃。在大模型训练场景中,经常会出现一种矛盾现象:即便配备了顶级的 H100 显卡,GPU 利用率却呈现大幅波动,甚至长时间徘徊在 30% 左右。此时若检查 CPU 监控,往往会发现某个核心已处于 100% 满载状态。这说明计算能力已经过剩,但数据供给速度跟不上,导致 GPU 在空转等待。面对 TB 级规模的数据集,如果依然沿用 read_csv 这种基础读取方式,性能必然受限。提升吞吐量的核心在于对数据管线(Pipeline)进行深层重构。

如何优化存储格式以提升数据读取效率?第一步是升级存储格式。CSV 或 JSON 等文本格式在处理海量数据时由于需要繁琐的字符串解析且不支持随机访问,效率极低。应当将数据转换为 Parquet 或 TFRecord。Parquet 采用列式存储,具备极高的压缩率,在读取特定字段时能大幅减少磁盘 IO。对于数亿行量级的数据,引入 Apache Arrow 会更有效,其“零拷贝(Zero-copy)”机制允许程序直接在内存中操作数据,跳过了序列化与反序列化的过程,从而为处理超大规模张量节省大量 CPU 周期。

第二步是在 PyTorch 层面优化 DataLoader 的配置。开发者常在 num_workers 参数上踩坑,盲目将其设为 32 或 64 等高数值,容易引发严重的内存碎片化甚至导致 OOM(内存溢出)。一个经过验证的稳妥配置如下:

from torch.utils.data import DataLoader

# 建议 num_workers 设置为 CPU 物理核心数的 1/2 或 1/4
# pin_memory 必须开启,它能让数据直接进入页锁定内存,加速拷贝到显存的速度
train_loader = DataLoader(
    dataset=train_dataset,
    batch_size=64,
    shuffle=True,
    num_workers=8,
    pin_memory=True,
    prefetch_factor=2  # 预取因子,确保每个 worker 提前准备 2 个 batch
)

在此配置中,pin_memory=True 与 prefetch_factor 的配合至关重要。利用异步预取(Prefetching)逻辑,可以在 GPU 计算当前 Batch 的同时,让 CPU 在后台准备好下一个 Batch 并推送到内存,从而实现计算与加载的重叠。

第三步是针对无法完全装入内存的超大文件,放弃一次性 read() 的做法,改用内存映射(Memory Mapping

0 回复

发表回复

支持 Markdown 格式
这个方向的上手步骤与避坑记录见用Claude整理的AI副业教程,有不少直接可参考的案例。