Fast contiguous and concurrent reads, VL data, nested groups and links, Python bindings #15

Merged
osobh merged 41 commits from feat/p2-perf-coverage into main 2026-09-26 14:57:01 +00:00
3 changed files with 14 additions and 9 deletions
Showing only changes of commit 37770f594a - Show all commits
+5 -5
View File
@@ -528,11 +528,11 @@ What this shows:
(about 880 MB/s) while h5py processes reach 4424 MB/s. Hyperslab (about 880 MB/s) while h5py processes reach 4424 MB/s. Hyperslab
reads, which bypass the `File`'s chunk cache, keep scaling, so the reads, which bypass the `File`'s chunk cache, keep scaling, so the
cache (one mutex and one 16 MiB budget per `File`, thrashed by 64 MiB cache (one mutex and one 16 MiB budget per `File`, thrashed by 64 MiB
datasets) is the suspect. **Fixed after these measurements datasets) is the suspect. The cause of the `--decode-threads 1`
(2026-09-26); the table above predates the fix and has not been ceiling was not the cache: every full read queued its chunks for the
re-measured.** The cause was not the cache: with `--decode-threads 1` pool's single rayon worker. That case was fixed after these
every full read queued its chunks for the pool's single rayon worker; measurements (2026-09-26, not yet re-measured here). With the default
see `docs/known-issues.md`. pool the gap to h5py processes remains (see `docs/known-issues.md`).
- *Contiguous reads are slow*: 2.5 GB/s for a single-threaded full read - *Contiguous reads are slow*: 2.5 GB/s for a single-threaded full read
against h5py's 9.8 GB/s (0.25x), and 0.12x for 256 x 256 hyperslabs. against h5py's 9.8 GB/s (0.25x), and 0.12x for 256 x 256 hyperslabs.
Threads close the gap (about 1.0x h5py at 16), but single-thread Threads close the gap (about 1.0x h5py at 16), but single-thread
+2 -1
View File
@@ -13,7 +13,8 @@
(`clawhdf5_format::parallel_read::pool_can_parallelise`). The `File`'s (`clawhdf5_format::parallel_read::pool_can_parallelise`). The `File`'s
chunk cache, the suspect in `docs/known-issues.md`, was not the cause: chunk cache, the suspect in `docs/known-issues.md`, was not the cause:
reads of datasets larger than its budget already skipped inserting, and reads of datasets larger than its budget already skipped inserting, and
its lookups cost a few percent at 16 threads. its lookups cost a few percent at 16 threads. Throughput with the default
pool is unchanged, and still short of an h5py process pool.
### Plugin filters (2026-09-26) ### Plugin filters (2026-09-26)
- **LZF, bitshuffle, bzip2 and Blosc read and write, in pure Rust.** Files - **LZF, bitshuffle, bzip2 and Blosc read and write, in pure Rust.** Files
+7 -3
View File
@@ -9,10 +9,10 @@ deleting it.
## Concurrent and contiguous read performance (measured 2026-09-26) ## Concurrent and contiguous read performance (measured 2026-09-26)
**Status:** first bullet fixed (2026-09-26), second open. Measured on **Status:** open; one cause of the first bullet fixed (2026-09-26). Measured on
tank with `concurrent_read` against h5py 3.16 / HDF5 2.0 (`BENCHMARKS.md`, tank with `concurrent_read` against h5py 3.16 / HDF5 2.0 (`BENCHMARKS.md`,
"Concurrent reads"): "Concurrent reads"):
- **Fixed 2026-09-26.** Full reads of chunked datasets from several threads - **Partly fixed 2026-09-26.** Full reads of chunked datasets from several threads
through one `File` stop scaling at about 4 threads (880 MB/s on deflate through one `File` stop scaling at about 4 threads (880 MB/s on deflate
data vs 4424 MB/s for 16 h5py processes). Hyperslab reads, which skip the data vs 4424 MB/s for 16 h5py processes). Hyperslab reads, which skip the
chunk cache, scale to 1244 MB/s, so the `File`'s shared chunk cache is the chunk cache, scale to 1244 MB/s, so the `File`'s shared chunk cache is the
@@ -22,7 +22,11 @@ tank with `concurrent_read` against h5py 3.16 / HDF5 2.0 (`BENCHMARKS.md`,
worker (per-thread CPU time: one thread did all the decoding, the 16 worker (per-thread CPU time: one thread did all the decoding, the 16
readers almost none). Hyperslab reads touch one chunk each and never used readers almost none). Hyperslab reads touch one chunk each and never used
the pool. Reads now decode on the calling thread when the pool has one the pool. Reads now decode on the calling thread when the pool has one
thread (`tests/single_thread_decode_pool.rs`). Datasets larger than the thread (`tests/single_thread_decode_pool.rs`). **Still open:** this
fixes only a one-thread pool. With the default pool, 16 reader threads
ran at about 2900 MB/s before and after the change, still short of 16
h5py processes (4424 MB/s); with a small pool (2-4 threads) readers
outside it still wait on its workers. Datasets larger than the
cache's budget were already read without inserting into it, and skipping cache's budget were already read without inserting into it, and skipping
its lookups entirely gained only a few percent at 16 threads. Remaining its lookups entirely gained only a few percent at 16 threads. Remaining
per-read overhead, not yet addressed: each full `read_f32` of a chunked per-read overhead, not yet addressed: each full `read_f32` of a chunked