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
2 changed files with 55 additions and 7 deletions
Showing only changes of commit dda28d6c72 - Show all commits
+46 -1
View File
@@ -484,7 +484,52 @@ explain the slower windows.
## Concurrent reads
### Results (2026-09-26, tank)
### Results after the read fixes (2026-09-26, tank, `408f69e`)
Same machine, files and commands as the first run below, re-run on an idle
tank (load average 1.60 at the start; the 1-minute figure rose to about 5
during the clawhdf5 runs, mostly their own threads) after two fixes:
contiguous reads back their output with transparent huge pages and copy
hyperslabs run by run, and full chunked reads no longer queue behind a
one-thread rayon pool. h5py was re-run in the same session.
Each read decoding on its calling thread (`--decode-threads 1`, like h5py):
| layout | mode | threads | clawhdf5 MB/s (eff) | h5py threads MB/s (eff) | h5py processes MB/s (eff) |
|---|---|---:|---:|---:|---:|
| deflate | distinct | 1 | 606 (1.00) | 432 (1.00) | 421 (1.00) |
| deflate | distinct | 4 | 1816 (0.75) | 428 (0.25) | 1654 (0.98) |
| deflate | distinct | 8 | 2943 (0.61) | 428 (0.12) | 3042 (0.90) |
| deflate | distinct | 16 | 2142 (0.22) | 375 (0.05) | 3083 (0.46) |
| deflate | same | 1 | 154 (1.00) | 130 (1.00) | 129 (1.00) |
| deflate | same | 4 | 599 (0.98) | 129 (0.25) | 499 (0.97) |
| deflate | same | 16 | 1592 (0.65) | 128 (0.06) | 1399 (0.68) |
| contiguous | distinct | 1 | 13665 (1.00) | 9490 (1.00) | 8781 (1.00) |
| contiguous | distinct | 16 | 12674 (0.06) | 2285 (0.02) | 6942 (0.05) |
| contiguous | same | 1 | 31991 (1.00) | 5087 (1.00) | 5078 (1.00) |
| contiguous | same | 16 | 237151 (0.46) | 4304 (0.05) | 35772 (0.44) |
With the default rayon pool: deflate `distinct` 2117 MB/s at 1 thread (4.9x
h5py), 3163 at 4, 2341 at 16 (0.76x h5py processes); deflate `same` 1439 MB/s
at 16; contiguous as above within a few percent.
Before -> after for clawhdf5 (`--decode-threads 1` unless noted):
contiguous full read at 1 thread 2495 -> 13665 MB/s (0.25x -> 1.44x h5py);
contiguous 256 x 256 hyperslabs at 1 thread 624 -> 31991 MB/s (0.12x ->
6.3x); deflate full reads at 8 threads 887 -> 2943 MB/s; deflate
hyperslabs at 16 threads 1244 -> 1592 MB/s.
Read with care:
- `contiguous same` reads 1024 slabs of one 64 MiB dataset over and over, so
it mostly measures copies out of the CPU's caches (the 7800X3D has 96 MiB
of L3); the per-call overhead is what differs (h5py's is about 50 us).
- At 16 threads every tool dropped in this run (h5py threads on contiguous
data from 8002 to 2285 MB/s, processes from 12846 to 6942), so the
16-thread rows are noisier than the others.
- Still behind: full reads of chunked data at 16 threads (0.69x-0.76x h5py
processes). See `docs/known-issues.md`.
### First run, before the read fixes (2026-09-26, tank, `91644d8`)
Measured on tank (AMD Ryzen 7 7800X3D, 8 cores / 16 threads, 61 GiB, Linux
7.0) at commit `91644d8`, load average 1.84 when the run started (the
+9 -6
View File
@@ -40,11 +40,12 @@ 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
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
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
thread (`tests/single_thread_decode_pool.rs`); re-measured at
`408f69e`, 8 threads went from 887 to 2943 MB/s (h5py processes: 3042).
**Still open:** at 16 threads full chunked reads reach 2142-2341 MB/s,
0.69x-0.76x 16 h5py processes (3083 MB/s in the same run), with the
default pool as with a one-thread one; 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
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
@@ -52,7 +53,9 @@ tank with `concurrent_read` against h5py 3.16 / HDF5 2.0 (`BENCHMARKS.md`,
the `f32` copy of it, and a new buffer per decoded chunk).
- Contiguous datasets read 4x slower than h5py on one thread (2.5 vs
9.8 GB/s full, 0.12x for 256 x 256 hyperslabs).
**Fixed 2026-09-26** (not yet re-measured for `BENCHMARKS.md`): full
**Fixed 2026-09-26** (re-measured on tank at `408f69e`: 13665 MB/s
full and 31991 MB/s for 256 x 256 hyperslabs on one thread, 1.44x and
6.3x h5py; `BENCHMARKS.md`): full
reads were dominated by 4 KiB page faults on the fresh output buffer,
which is now backed by transparent huge pages as numpy's is; hyperslab
reads copied the selection three times, element by element, and now copy