如何优化HDF5文件中百万张图片的随机读取性能?
把数百万张图片存在HDF5里,随机顺序读取比顺序慢一大截?这是因为HDF5默认的独立数据集存储方式在随机访问时会产生大量磁盘寻道开销,试试下面这些针对性的优化手段:
合并成单个连续数据集
你现在是用独立key存每张图,每个图都是单独的HDF5数据集,随机读的时候得频繁跳转到不同的磁盘位置,自然慢。建议把所有图片打包成一个多维数组(比如(总图片数, 高, 宽, 通道数)),存成单个HDF5数据集。这样随机读取时直接通过索引定位,能大幅减少磁盘寻道。示例代码:# 写入时合并为单数据集 with h5py.File("images.h5", "w") as hf: hf.create_dataset("all_images", data=all_imgs_array, compression="lzf") # 随机读取时直接取索引 with h5py.File("images.h5", "r") as hf: rand_indices = np.random.permutation(len(all_imgs_array))[:16] imgs = hf["all_images"][rand_indices]启用分块(Chunking)优化
如果没法合并成单数据集,给每个数据集设置合理的分块大小,或者让HDF5自动分块。分块能让HDF5更高效地组织磁盘上的数据,减少随机访问的IO开销。创建数据集时加chunks=True就行:with h5py.File("images.h5", "w") as hf: for idx, img in enumerate(img_list): hf.create_dataset(f"img_{idx}", data=img, chunks=True, compression="lzf")调大HDF5缓存参数
HDF5默认的缓存比较小,开启更大的元数据和数据缓存,能减少重复读磁盘的次数。根据你的内存情况调整缓存大小,比如设置1GB数据缓存:with h5py.File("images.h5", "r", rdcc_nbytes=1024**3, rdcc_nslots=1000000) as hf: # 读取操作rdcc_nbytes是数据缓存大小,rdcc_nslots是缓存条目数,数值越大缓存效果越好,别超过可用内存就行。换用更适合随机读的压缩算法
高压缩比的算法(比如高等级gzip)解压慢,会拖慢随机读取。换成lzf这种压缩比适中但解压快的算法,或者直接关闭压缩(如果磁盘空间够的话),都能提升性能:# 用lzf压缩 hf.create_dataset(f"img_{idx}", data=img, compression="lzf")预加载元数据
每次读新的key都要去磁盘查元数据,预加载所有数据集的元信息能避免这个开销:with h5py.File("images.h5", "r") as hf: # 提前把所有数据集对象存到字典里 dataset_map = {key: hf[key] for key in hf.keys()} keys = list(dataset_map.keys()) shuffle(keys) for key in keys[:16]: np.array(dataset_map[key])换SSD存储
机械硬盘的随机IO性能天生弱,把HDF5文件移到SSD上,随机读取的寻道时间会大幅降低,这是最直接的硬件优化手段。
原测试代码
with h5py.File("/slowdata/caid_2024/GenImage_compressed.h5") as hf: keys = list(hf.keys()) from random import shuffle sleep(0.1) for i, key in enumerate(keys): if i == 16: break np.array(hf[key]) # Random order now shuffle(keys) sleep(0.1) for i, key in enumerate(keys): if i == 16: break np.array(hf[key]) sleep(0.1)
时间线对比图

内容的提问来源于stack exchange,提问作者Rodolphe LAMPE

