Implement Lazy Loading of HDF5 Assets - #1465
Open
genematx wants to merge 7 commits into
Open
Conversation
genematx
marked this pull request as ready for review
August 11, 2026 16:01
Member
A) Items in If (A) then it sounds like |
Contributor
Author
|
Interesting. I always thought of |
genematx
added a commit
to genematx/tiled
that referenced
this pull request
Aug 11, 2026
genematx
added a commit
to genematx/tiled
that referenced
this pull request
Aug 11, 2026
genematx
force-pushed
the
perf-data-sources
branch
from
August 11, 2026 19:14
2e13a87 to
60f6473
Compare
Extend the catalog's lazy per-asset read path to HDF5 array data sources, which concatenate files along the leading axis with possibly non-uniform per-file extents. Add an optional per-asset `extents` property so a read opens only the files a slice touches; fall back to structure chunks or the grid geometry, and to a full build when files can not be located. The catalog no longer gates on a `chunks` property and derives the file count from an aggregate asset query, extending lazy loading to plain stacks while keeping adapter internals opaque to the catalog.
file_indices_for_slice now receives the data source parameters and declines file selection when a slice/squeeze transform is present. Those transforms reshape each file when building the served array, so the served axis 0 is no longer a plain concatenation of whole files and the requested slice can not be mapped back onto the stored per-file boundaries -- which structure.chunks[0] alone can still appear to allow. The catalog threads the parameters through so the adapter falls back to a full build in this case.
genematx
force-pushed
the
perf-data-sources
branch
from
August 12, 2026 14:10
dc22e4b to
804f39e
Compare
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.
Summary
Follow-up on #1463.
#1463 made many-file HDF5 reads reuse a cached Dask graph, but the first read of a dataset in a fresh process still opened every constituent file once to discover specs before the cache could help. This PR extends the lazy asset-resolution path (introduced for file sequences in #1463) to multi-file HDF5 array datasets, so the first read opens only the files its slice actually touches.
Why HDF5 needed its own path
A file sequence stacks uniform frames — one file adds exactly one row on a new leading axis — so the catalog's
1 file = 1 rowgeometry recovers file boundaries from the structure alone. HDF5 files instead concatenate along the array's leading axis, and each file may contribute a different number of rows, so the boundaries can not be read offstructure.chunks.What changed
catalog/adapter.py—_get_lazy_adapteris now adapter-agnostic. It no longer gates on achunksproperty; it derives the file count from a single aggregate asset query (which also performs the existingnumcontiguity check), then asks the adapter classfile_indices_for_slice(structure, n_files, slice, properties=...)which files a slice touches. Only the adapter interpretsproperties. It also forwards the data sourceparameters(as the eager path does) so adapters that need them (e.g. HDF5'sdataset) are configured. This additionally extends lazy loading to plain (non-chunks) file stacks.adapters/hdf5.py—HDF5ArrayAdapteropts in viasupports_lazy_assetsand gains_file_layout, which resolves each file's extent along the leading axis in priority order:extentsproperty — the authoritative per-file row counts (sum(extents) == shape[0]); supports genuinely non-uniform files,structure.chunks[0]when it has exactly one chunk per file,None→ the catalog falls back to the full build._lazy_stack_from_structurebuilds the file-stacked Dask array from the known structure without opening any file for specs: touched files get a whole-file read task (URI resolved to a path lazily, inside the task), untouched files get a placeholder block that Dask culls once the read slices the reshaped array (and that raises loudly if ever computed, so a geometry mismatch can never serve wrong data).adapters/utils.py— the file-alignment geometry shared by the sequence and HDF5 adapters is extracted intogrid_shape_for_files(a pure function of shape + file count, no I/O).On the optional
extentspropertyextentsis optional and fully adapter-contained; the catalog stays ignorant of it. Both datasets benchmarked below resolve without it —enc1via one leading chunk per file (path 2),fluorvia the grid geometry (path 3). It is needed only for genuinely non-uniform flat files (differing per-file row counts that are not one-chunk-per-file). A writer can populateproperties["extents"]to unlock the lazy path there; that consolidator change is a planned follow-up (not in this PR).Results
Cold first read (fresh process),
arr[0:1]Measured on a staging catalog by patching
h5py.Fileto count opens:enc1(3 files,(3, 11))enc1(153 files,(153, 223))fluor(153 files,(153, 223, 8, 4096)f8)h5py opens are the deterministic metric: before scales with the file count (every file opened for specs, ~2× for external links); after is a constant 2, independent of how many files back the dataset. Absolute times are filesystem-latency dependent (storage was heavily loaded during the before run), but the open-count reduction is structural.
Value equivalence (lazy vs. eager full build)
The lazy read was compared byte-for-byte against a forced eager full-adapter build across first / middle / last / arbitrary-window / leading-plus-trailing / deep multi-axis slices. All identical:
fluor(grid)[0:1],[76:78],[152:153],[75:77],[0:2, 0:5],[10:11, 100:110, 3:6]0.0enc1(chunks extents)[0:1],[100:103],[152:153],[50:52, 10:20]0.0Tests
pytest tests/test_catalog.py tests/test_hdf5.py tests/test_tiff.py tests/test_slicer.py→ 263 passed, 29 skipped.test_catalog.pycover the grid path (opens only touched files), multi-chunk files, the non-file-aligned fallback, theextentsproperty (parameterized, incl. non-uniform), and the "non-uniform requiresextents" case.Checklist
Add the ticket number which this PR closes to the comment section