DuckSoft's Miscellanies Ex nihilo ad astra.

使用 btrfs 提升 HDF5 透明压缩百倍性能

众所周知,HDF5(由 h5py 包提供支持)是 Python 中比较高效地存储数据集的方案。由于 HDF5 本身支持分块、透明压缩等高级功能,加之可以将大量零散的 numpy 格式的数据集打包在一个文件中,其受到了不少深度学习应用人士的追捧。但隐藏在 h5py 中的尴尬的透明压缩实现,导致当开启透明压缩时,性能下降极其严重。

举一个之前做过的例子:帮人处理一套数据集,简单从单色 TIFF 格式转为 HDF5 格式,没有开启透明压缩时,tqdm 监控下的速度是 3it/s,也就是每秒大概能处理三张图;开启了 gzip 透明压缩后,这速度就变成了大概每 30 秒才能处理完一张图。估算结果表明,不压缩时总共需要约 4 小时时间,而压缩时需要耗约 350 小时——近半个月。这个烂性能是完全不能忍受的。

不仅如此,当时东家的破服务器硬盘还只剩下了 1TB,而初步估算下来,如果不开压缩,处理完之后的数据集要吃掉 3TB 的空间!要知道,HDF5 格式最大的优点也正是它最大的缺点——单文件。如果硬盘装不下一个文件,那事情就变得非常难办。

这是一个死局:你压缩了,必然在合理时间内跑不完。你不压缩,东家的硬盘不够大,同样无法完成任务。

在联系并爆锤东家让他换全固态、加 SAS 硬盘之前,我突然想到了一个每天都在用的好东西——btrfs。这优雅的文件系统是支持透明压缩的!既然 h5py 不擅长进行实时压缩,那就干脆不让他干这个活不就好了吗。

我确认了一个细节:/home 目录是挂载在机械硬盘上的,并有 1.1TB 的余量可用。下面进行操作:

实操

使用 dd 命令,创建一个稀疏的块文件:

# 在 /home/user 文件夹下创建名为 vda 的块文件
# 块大小 bs=1G,跳过 800 块后再写出 0 块
# 实际大小 800G,但不占用空间(稀疏)
dd if=/dev/zero of=/home/user/vda bs=1G seek=800 count=0

之所以使用稀疏的块文件,是因为这样可以不需要写出 800G 空间到硬盘,节约时间和资源消耗。

之后,在块文件上创建文件系统:

# 创建 btrfs
mkfs.btrfs /home/user/vda

将创建好的文件系统进行挂载,并设定实时压缩:

# 挂载到 /mnt 下,设定压缩算法为 zstd
mount -o compress=zstd /home/user/vda /mnt

完成这些之后,我们就在 /mnt 目录下有了一个支持透明压缩的文件系统了。

评估

将 HDF5 数据集的生成路径放在这里,再正常开启数据集预处理程序,通过对比不难发现,使用 btrfs 的透明压缩与不使用任何透明压缩,数据集处理的性能其实没有明显区别,透明压缩并没有影响到 HDF5 生成的性能;但将 btrfs 的透明压缩与 h5py 自带的透明压缩对比时,btrfs 的效率要高出近百倍。通过 compsize 工具的分析表明,btrfs 的透明压缩不仅快,而且生成的文件的尺寸相比 gzip 压缩还要更小,可谓是多快好省、简单粗暴的解决办法了。

总结

正所谓活人不能被尿憋死,当发现一条路行不通的时候,如果很难凿开面前的路,不妨换条路试试,也许会收获意外的惊喜。

comments powered by Disqus