Implement a direct reader for HDF5 files - #759
Open
JamesWrigley wants to merge 2 commits into
Open
Conversation
This takes a bit more disk space but is needed to get actual HDF5 chunks on disk.
For specific kinds of datasets, this retrieves the chunk information from HDF5 and then reads it manually using `os.preadv()`. Reading it ourselves releases the GIL so reads can be multithreaded and be much faster.
| np.s_[:16, :8], # Part of each row as well | ||
| np.s_[::4], # Strided | ||
| np.s_[5], # A single row, dropping that dimension | ||
| np.s_[[1, 3, 7]], # Arbitrary rows |
Member
|
Nice, I will take a proper look after a long weekend. I also coincidentally did something similar with writing recently - set up an empty dataset in advance, then have parallel processes writing into it directly with pwrite(): https://git.xfel.eu/kluyvert/extra-azint/-/blob/main/extra_azint/output.py?ref_type=heads |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
For specific kinds of datasets, this retrieves the chunk information from HDF5 and then reads it manually using
os.preadv(). Reading it ourselves releases the GIL so reads can be multithreaded and be much faster.This originated from a multiprocessing streamer that I wrote ages ago, and when cleaning it up recently I figured it would be nice to reuse that machinery to make to make
.xarray()and.ndarray()faster as well. It didn't go very well though because multiprocessing requires shared memory and that's limited on most systems, and since reading from KeyData objects is so common I didn't want to require shared memory for that. Long story short, I ended up ✨ vibe coding ✨ a Python native HDF5 reader that can be used with threads instead of processes.Example performance improvement for compressed AGIPD data:

And uncompressed JF data:

Times are with warm data. With cold data there's also an improvement but it's like 2-5x instead of 10x.
Design notes:
direct_read.read()which can transparently fall back to h5py for unsupported datasets (strings, etc).FileAccessobjects. It's reused across reads by DatasetReader.EXTRA_DATA_DIRECT_READ_DEBUGenvironment variable can be used to figure out whether doing direct reads failed and for what reason.Performance notes:
SPLIT_BYTESsize to specify the maximum size of a chunk (file, not HDF5) that we read.gpfs_fcntl()exists 🐙 The header file on maxwell is at/usr/lpp/mmfs/include/gpfs_fcntl.h. I tried a few flags in there but none made any performance difference.GPFS_CLEAR_FILE_CACHEis amazeballs though, that will clear the file cache so you can benchmark cold reads easily.For reviewers, I tried to keep the commits atomic so I'd recommend reviewing them one-by-one.
direct_read.pyin particular is mostly vibe-coded, with various tweaks from me (not that I'm an HDF5 expert).I believe we could also reuse this in the DAMNIT API. It should already work for regular ndarray's. For DataArray's I think we can use open_dataarray() to lazily create the DataArray object with the metadata, load the main dataset with the direct reader, and then copy the lazily-opened object and swap in the main array.
I'm not quite satisfied with the streamer yet so I'll leave that for a later PR.