在上一篇中我们简要介绍了 HDF5 和 h5py 的基本操作,下面我们将介绍利用 mpi4py 和 h5py 进行并行分布式的 HDF5 文件操作。
使用 mpi4py 进行一般并行文件操作在前面已经作了相应的介绍,并行 HDF5 文件的相关操作在很多方面是与一般的并行文件操作相同或满足相似的约束条件。但是如果使用 mpi4py 和 h5py 进行并行 HDF5 文件操作,则 HDF5 文件的很多并行操作细节对使用者来说是透明的,无需用户关心,因此大大降低了并行 HDF5 文件操作的难度。
使用 h5py 进行并行 HDF5 操作
要并行地打开/创建一个 HDF5 文件,可以使用上一篇介绍的方法并且设置参数 driver
为 'mpio' 及 comm
为一个有效的通信子对象。
注意打开/创建文件是一个集合操作,通信子对象上的所有进程必须同时参与。
打开文件后就可以执行并行的文件读写操作,如创建 group,创建/读取 dataset,设置/读取 attribute 等,但是需要注意和区分两类操作方法:集合操作和独立操作。任何涉及改动文件结构和文件元数据的操作都必须是集合操作(即通信子对象上的所有进程必须同时参与),如创建 group,创建dataset,设置 attribute 等,但是每个进程可以独立地向已经存在的 dataset 中写入数据,或者从其中读取数据。
从版本 1.8.9+ 开始,HDF5 支持原子操作模式,原子模式可以更严格的方式保证文件操作的一致性。并行文件操作的原子性和一致性在前面的访问文件数据方法中作了相应的介绍。要在 h5py 中打开原子操作模式,只需设置文件操作句柄的 atomic 属性为 True。
例程
下面给出使用 h5py 进行并行 HDF5 操作的使用例程。
# parallel_h5.py
"""
Demonstrates how to use parallel HDF5 with h5py.
Run this with 4 processes like:
$ mpiexec -n 4 python h5py_demo.py
"""
import os
import numpy as np
import h5py
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.rank
size = comm.size
N = 10
# N = 2**29
file_name = 'test.hdf5'
# create a new HDF5 file collectively
f = h5py.File(file_name, driver='mpio', comm=comm)
# use atomic mode
f.atomic = True
# create a new group collectively
f.create_group('grp')
# create a empty dataset in group "/grp" collectively
f.create_dataset('grp/dset', shape=(size, N), dtype=np.int32)
# set an attribute of the dataset grp/dset collectively
f['grp/dset'].attrs['a'] = 1
data = np.arange(N, dtype=np.int32)
# write data to dataset grp/dset
f['grp/dset'][rank, :] = data
# rank 0 changes a slice of the dataset individually
if rank == 0:
f['grp/dset'][1, :] += data
# synchronize here
comm.Barrier()
# rank 2 reads the changed slice
if rank == 2:
print f['grp/dset'][1, :]
# read the attrs
if rank == 1:
print f['grp/dset'].attrs['a']
# close file collectively
f.close()
# remove the created file
if rank == 0:
os.remove(file_name)
运行结果如下:
$ mpiexec -n 4 python parallel_h5.py
[ 0 2 4 6 8 10 12 14 16 18]
1
2 GB dataset 限制
以上的例程运行正常,但是当我们将 N 设置成 229 或更大,这样每个进程将要写入文件的数据 ≥ 2 GB,则执行结果会如下:
$ mpiexec -n 4 python parallel_h5.py
Traceback (most recent call last):
File "parallel_h5.py", line 37, in <module>
f['grp/dset'][rank, :] = data
File "/opt/python-2.7.5-mkl/lib/python2.7/site-packages/h5py/_hl/dataset.py", line 551, in __setitem__
self.id.write(mspace, fspace, val, mtype)
File "h5d.pyx", line 217, in h5py.h5d.DatasetID.write (h5py/h5d.c:3225)
Traceback (most recent call last):
File "parallel_h5.py", line 37, in <module>
f['grp/dset'][rank, :] = data
File "/opt/python-2.7.5-mkl/lib/python2.7/site-packages/h5py/_hl/dataset.py", line 551, in __setitem__
Traceback (most recent call last):
File "parallel_h5.py", line 37, in <module>
self.id.write(mspace, fspace, val, mtype)
File "h5d.pyx", line 217, in h5py.h5d.DatasetID.write (h5py/h5d.c:3225)
f['grp/dset'][rank, :] = data
File "/opt/python-2.7.5-mkl/lib/python2.7/site-packages/h5py/_hl/dataset.py", line 551, in __setitem__
self.id.write(mspace, fspace, val, mtype)
File "h5d.pyx", line 217, in h5py.h5d.DatasetID.write (h5py/h5d.c:3225)
Traceback (most recent call last):
File "parallel_h5.py", line 37, in <module>
f['grp/dset'][rank, :] = data
File "/opt/python-2.7.5-mkl/lib/python2.7/site-packages/h5py/_hl/dataset.py", line 551, in __setitem__
self.id.write(mspace, fspace, val, mtype)
File "h5d.pyx", line 217, in h5py.h5d.DatasetID.write (h5py/h5d.c:3225)
File "_proxy.pyx", line 120, in h5py._proxy.dset_rw (h5py/_proxy.c:1793)
File "_proxy.pyx", line 120, in h5py._proxy.dset_rw (h5py/_proxy.c:1793)
File "_proxy.pyx", line 120, in h5py._proxy.dset_rw (h5py/_proxy.c:1793)
File "_proxy.pyx", line 120, in h5py._proxy.dset_rw (h5py/_proxy.c:1793)
File "_proxy.pyx", line 93, in h5py._proxy.H5PY_H5Dwrite (h5py/_proxy.c:1603)
File "_proxy.pyx", line 93, in h5py._proxy.H5PY_H5Dwrite (h5py/_proxy.c:1603)
File "_proxy.pyx", line 93, in h5py._proxy.H5PY_H5Dwrite (h5py/_proxy.c:1603)
File "_proxy.pyx", line 93, in h5py._proxy.H5PY_H5Dwrite (h5py/_proxy.c:1603)
IOError: can't prepare for writing data (Dataset: Write failed)
IOError: can't prepare for writing data (Dataset: Write failed)
IOError: can't prepare for writing data (Dataset: Write failed)
IOError: can't prepare for writing data (Dataset: Write failed)
程序运行出错,这是因为并行 HDF5 文件的读写操作单个进程一次操作的数据量有 2 GB 的大小限制,超过 2 GB 就会出错。其实这并不是并行 HDF5 本身的问题,更不是 h5py 的问题,而是 MPI 的问题,更准确地说是 MPI 的并行 I/O 操作工具 ROMIO 的问题(ROMPIO 是目前几乎所有 MPI 实现的默认或唯一并行 I/O 工具)。ROMIO 的绝大部分 I/O 操作单次允许操作的数据量都限制在 2 GB 之内,所以除非在将来改进 ROMIO,这一限制会一直存在。所以我们在进行并行 HDF5 操作时,应该注意单次读写的数据量不要超过 2 GB,如果要读写超过 2 GB 的数据,可以利用类似 numpy 数组切片的功能进行多次操作。
以上我们介绍了利用 mpi4py 和 h5py 进行并行分布式的 HDF5 文件操作,在下一篇中我们将介绍 caput 中的 memh5 模块,其中提供若干功能强大的工具可以使我们在内存中操作与 HDF5 文件类似的数据结构,并提供该数据结构到磁盘中 HDF5 文件之间的映射,当然这些操作都是可以并行分布式地进行的。
网友评论