Installing Parallel h5py
This guide has been shortened and adapted from a challenge in OLCF’s Hands-On with Summit GitHub repository (Python: Parallel HDF5).
Warning
The guide is designed to be followed from start to finish, as certain steps must be completed in the correct order before some commands work properly.
Overview
This guide teaches you how to build a personal, parallel-enabled version of h5py and how to write an HDF5 file in parallel using mpi4py and h5py. Although Summit is being used in this guide, all of the concepts still apply to other OLCF systems.
In this guide, you will:
- Learn how to install mpi4py on Summit
- Learn how to install parallel h5py on Summit
- Test your build with Python scripts
Parallel HDF5
Scientific simulations generate large amounts of data on Summit (about 100 Terabytes per day for some applications). Because of how large some datafiles may be, it is important that writing and reading these files is done as fast as possible. Less time spent doing input/output (I/O) leaves more time for advancing a simulation or analyzing data.
One of the most utilized file types is the Hierarchical Data Format (HDF), specifically the HDF5 format. HDF5 is designed to manage large amounts of data and is built for fast I/O processing and storage. An HDF5 file is a container for two kinds of objects: “datasets”, which are array-like collections of data, and “groups”, which are folder-like containers that hold datasets and other groups.
There are various tools that allow users to interact with HDF5 data, but we will be focusing on h5py – a Python interface to the HDF5 library. h5py provides a simple interface to exploring and manipulating HDF5 data as if they were Python dictionaries or NumPy arrays. For example, you can extract specific variables through slicing, manipulate the shapes of datasets, and even write completely new datasets from external NumPy arrays.
Both HDF5 and h5py can be compiled with MPI support, which allows you to optimize your HDF5 I/O in parallel. MPI support in Python is accomplished through the mpi4py package, which provides complete Python bindings for MPI. Building h5py against mpi4py allows you to write to an HDF5 file using multiple parallel processes, which can be helpful for users handling large datasets in Python. h5Py is available after loading the default Python module on either Summit or Andes, but it has not been built with parallel support.
Setting up the environment
Warning
Before setting up your conda environment, you must exit and log back in to Summit so that you have a fresh login shell.
This is to ensure that no previously activated conda environments exist in your $PATH
environment variable.
Building h5py from source is highly sensitive to the current environment variables set in your profile. Because of this, it is extremely important that all the modules and conda environments you plan to load are done in the correct order, so that all the environment variables are set correctly. First, unload all the current modules that you may have automatically loaded on Summit and then immediately load the default modules.
$ module purge
$ module load DefApps
Next, load the gnu compiler module (most Python packages assume GCC), hdf5 module (necessary for h5py), and the python module (allows you to create a new conda environment):
$ module load gcc
$ module load hdf5
$ module load python
Loading the python module puts you in a “base” conda environment, but you need to create a new environment using the conda create
command:
$ conda create -p /ccs/proj/<project_id>/<user_id>/conda_envs/summit/h5pympi-summit python=3.8
Note
As noted in the Python on OLCF Systems page, it is highly recommended to create new environments in the “Project Home” directory.
After following the prompts for creating your new environment, you can now activate it:
$ source activate /ccs/proj/<project_id>/<user_id>/conda_envs/summit/h5pympi-summit
Installing mpi4py
Now that you have a fresh conda environment, you will next install mpi4py from source into your new environment. To make sure that you are building from source, and not a pre-compiled binary, use pip:
$ MPICC="mpicc -shared" pip install --no-binary=mpi4py mpi4py
The MPICC
flag ensures that you are using the correct C wrapper for MPI on the system.
Building from source typically takes longer than a simple conda install
, so the download and installation may take a couple minutes.
If everything goes well, you should see a “Successfully installed mpi4py” message.
Installing h5py
Next, install h5py from source.
Because h5py depends on NumPy, install an optimized version of the NumPy package using conda install
:
$ conda install -c defaults --override-channels numpy
The -c defaults --override-channels
flags ensure that conda will search for NumPy only on the “defaults” channel.
Installing NumPy in this manner results in an optimized NumPy that is built against linear algebra libraries, which performs operations much faster.
Next, you are finally ready to install h5py from source:
$ HDF5_MPI="ON" CC=mpicc pip install --no-binary=h5py h5py
The HDF5_MPI
flag is the key to telling pip to build h5py with parallel support, while the CC
flag makes sure that you are using the correct C wrapper for MPI.
This installation will take much longer than both the mpi4py and NumPy installations (5+ minutes if the system is slow).
When the installation finishes, you will see a “Successfully installed h5py” message.
Testing parallel h5py
Test your build by trying to write an HDF5 file in parallel using 42 MPI tasks.
First, change directories to your GPFS scratch area:
$ cd $MEMBERWORK/<YOUR_PROJECT_ID>
$ mkdir h5py_test
$ cd h5py_test
Let’s test that mpi4py is working properly first by executing the example Python script “hello_mpi.py”:
# hello_mpi.py
from mpi4py import MPI
comm = MPI.COMM_WORLD # Use the world communicator
mpi_rank = comm.Get_rank() # The process ID (integer 0-41 for a 42-process job)
print('Hello from MPI rank %s !' %(mpi_rank))
To do so, submit a job to the batch queue with “submit_hello.lsf”:
$ bsub -L $SHELL submit_hello.lsf
Example “submit_hello.lsf” batch script:
#!/bin/bash
#BSUB -P <PROJECT_ID>
#BSUB -W 00:05
#BSUB -nnodes 1
#BSUB -J mpi4py
#BSUB -o mpi4py.%J.out
#BSUB -e mpi4py.%J.err
cd $LSB_OUTDIR
date
module load gcc
module load hdf5
module load python
source activate /ccs/proj/<project_id>/<user_id>/conda_envs/summit/h5pympi-summit
jsrun -n1 -r1 -a42 -c42 python3 hello_mpi.py
If mpi4py is working properly, in mpi4py.<JOB_ID>.out
you should see output similar to:
Hello from MPI rank 21 !
Hello from MPI rank 23 !
Hello from MPI rank 28 !
Hello from MPI rank 40 !
Hello from MPI rank 0 !
Hello from MPI rank 1 !
Hello from MPI rank 32 !
.
.
.
If you see this, great, it means that mpi4py was built successfully in your environment.
Finally, let’s see if you can get these tasks to write to an HDF5 file in parallel using the “hdf5_parallel.py” script:
# hdf5_parallel.py
from mpi4py import MPI
import h5py
comm = MPI.COMM_WORLD # Use the world communicator
mpi_rank = comm.Get_rank() # The process ID (integer 0-41 for a 42-process job)
mpi_size = comm.Get_size() # Total amount of ranks
with h5py.File('output.h5', 'w', driver='mpio', comm=MPI.COMM_WORLD) as f:
dset = f.create_dataset('test', (42,), dtype='i')
dset[mpi_rank] = mpi_rank
comm.Barrier()
if (mpi_rank == 0):
print('42 MPI ranks have finished writing!')
The MPI tasks are going to write to a file named “output.h5”, which contains a dataset called “test” that is of size 42 (assigned to the “dset” variable in Python). Each MPI task is going to assign their rank value to the “dset” array in Python, so you should end up with a dataset that contains 0-41 in ascending order.
Time to execute “hdf5_parallel.py” by submitting “submit_h5py.lsf” to the batch queue:
$ bsub -L $SHELL submit_h5py.lsf
Example “submit_h5py.lsf” batch script:
#!/bin/bash
#BSUB -P <PROJECT_ID>
#BSUB -W 00:05
#BSUB -nnodes 1
#BSUB -J h5py
#BSUB -o h5py.%J.out
#BSUB -e h5py.%J.err
cd $LSB_OUTDIR
date
module load gcc
module load hdf5
module load python
source activate /ccs/proj/<project_id>/<user_id>/conda_envs/summit/h5pympi-summit
jsrun -n1 -r1 -a42 -c42 python3 hdf5_parallel.py
Provided there are no errors, you should see “42 MPI ranks have finished writing!” in the h5py.<JOB_ID>.out
output file, and there should be a new file called “output.h5” in your directory.
To see explicitly that the MPI tasks did their job, you can use the h5dump
command to view the dataset named “test” in output.h5:
$ h5dump output.h5
HDF5 "output.h5" {
GROUP "/" {
DATASET "test" {
DATATYPE H5T_STD_I32LE
DATASPACE SIMPLE { ( 42 ) / ( 42 ) }
DATA {
(0): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
(19): 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
(35): 35, 36, 37, 38, 39, 40, 41
}
}
}
}
If you see the above output, then the build was a success!