因为默认不启用分块和压缩,create_dataset()会一次性加载全部数据进内存并串行刷盘,导致卡顿、oom或频繁gc;必须显式设置chunks参数(如chunks=(10000,128))并批量切片写入(dset[i:end] = batch),避免单点赋值触发多次元数据查找。

为什么直接用 h5py.File().create_dataset() 写大数据会慢?
因为默认行为是把整个数据一次性加载进内存再写入,尤其当 data.shape 超过几百 MB 时,Python 进程可能卡住、OOM 或触发频繁 GC。更关键的是,create_dataset() 默认不启用 chunking 和 compression,底层 HDF5 无法做分块 I/O 优化,写入变成串行大块刷盘。
- 必须显式设置
chunks=True(或指定具体尺寸),否则后续无法以切片方式高效追加/更新 - 小文件(50MB 的数组,
chunks=(1000, -1)比chunks=True更可控 - 避免在循环里反复调用
dataset[i] = x——单点赋值触发多次元数据查找,改用整块切片赋值
如何用 h5py 分块写入百万级样本?
核心是预分配 dataset + 分批次写入切片。假设你有一批 shape 为 (N, D) 的浮点特征,N=2e6,D=128:
import h5py
import numpy as np
<p>with h5py.File("features.h5", "w") as f:</p><h1>预分配,显式指定 chunks:按行分块,每块 10000 行</h1><pre class="brush:python;toolbar:false;">dset = f.create_dataset(
"X",
shape=(2_000_000, 128),
dtype="float32",
chunks=(10000, 128), # ← 关键:沿第一维切分
compression="lzf" # 可选,lzf 比 gzip 快得多,压缩率适中
)
# 分批写入(例如每次 50000 行)
batch_size = 50000
for i in range(0, 2_000_000, batch_size):
end = min(i + batch_size, 2_000_000)
batch = load_batch(i, end) # 你的数据加载逻辑
dset[i:end] = batch # ← 整块赋值,非循环逐行
注意:chunks=(10000, 128) 不代表必须按 10000 行写入,只是 HDF5 内部组织单位;dset[i:end] 会自动映射到对应 chunk 区域,效率远高于单元素索引。
zarr 能比 h5py 更快吗?什么场景该换?
纯写入吞吐量上,zarr 在多核 CPU + SSD 场景下常比 h5py 高 2–5 倍,因为它原生支持并行 chunk 写入、无全局文件锁、且默认启用 Blosc 压缩(比 LZF 更快)。但代价是:不兼容传统 HDF5 生态(如 MATLAB、IDL)、部分科学计算库不直接支持。
- 如果你只用 Python,且需要快速 dump 中间特征供后续训练读取 → 优先试
zarr.open_array(..., compressor=blosc.Blosc(cname='lz4')) - 如果下游要交给 C/Fortran 程序读,或需用
h5dump/h5ls查看 → 死守h5py,调优 chunk size 和压缩器即可 -
zarr的store参数支持直接写入 S3/HTTP,而h5py需靠h5pyd,这是云原生 pipeline 的关键分水岭
写入后校验和性能陷阱
别在写完立刻用 np.array_equal(dset[:], expected) 全量比对——这会强制读回全部数据,内存翻倍,且掩盖真实写入瓶颈。正确做法:
- 写入时同步记录每批的
np.mean()和np.std(),最后比对摘要统计量 - 用
h5py.File(..., swmr=True)打开文件后,启动另一个进程用h5ls -r features.h5快速验证结构是否完整 - 特别注意 Windows 上
h5py的默认驱动是sec2,并发写入易出错;Linux/macOS 推荐显式加driver="family"或改用core驱动做内存映射测试
真正卡住的往往不是 HDF5 本身,而是 chunk size 与访问模式不匹配、压缩器选错、或者误把生成器当数组传给 create_dataset —— 这些细节比“用不用多线程”影响更大。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











