docs: fix untraceable benchmark claims, add dual-audience framing, validate on second machine
- README's "HDF5 Core I/O" table claimed 19ns/2,080µs labeled 308× (real ratio ~109,000×) and a 313ns zero-copy mmap figure — neither traced to any dated benchmark in BENCHMARKS.md. Replaced the table wholesale with the existing "vs libhdf5 Summary" figures, relabeled from "h5py/C HDF5" to "libhdf5" (BENCHMARKS.md never benchmarks against h5py, only libhdf5 directly). - Added two new Criterion benchmarks to close the coverage gaps that produced the untraceable numbers: metadata_open_from_disk (I/O-inclusive, fair clawhdf5-vs-libhdf5 file-open comparison) and metadata_parse_in_memory (clawhdf5-only, explicitly labeled as excluding I/O) in h5bench_meta.rs; read_zerocopy_mmap in h5bench_read.rs (forces real page-ins by summing elements rather than just returning a slice length — the mmap path turns out to be slower than a plain copy at these sizes, an honest, unflattering but real result now documented instead of a fabricated 313ns). - Re-ran the full existing benchmark suite plus the two new ones on a second, independently administered machine (tank: Ryzen 7 7800X3D) to validate the numbers before publishing them. 5 of 6 rows landed within ~15% of the original i7-12650H figures; recorded both in BENCHMARKS.md's new "Independent Validation" section. README now cites the tank numbers. - Added a short top-of-file README callout naming both halves of the project (general-purpose HDF5 library vs. agent memory layer) with links to BENCHMARKS.md and the Crate Map, so a data-infra reader isn't 60% through a memory-store pitch before finding the part relevant to them. - Added one factual, no-names line noting benchmark numbers are being validated in collaboration with HDF5 Group engineers. - Fixed the same untraceable "2-300x faster than h5py/C HDF5" / "313 ns" claims in docs/QUICKSTART.md, one click from the README's own "New here?" link. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
6b1ea450f5
commit
88195d1c33
@@ -564,3 +564,88 @@ to the page cache. There is no algorithmic headroom above ~1.7 GiB/s on this har
|
|||||||
- libhdf5 f64 read comparison excluded — clawhdf5's f32 datatype encoding differs from libhdf5's (known compatibility gap). f64 results are clawhdf5-only.
|
- libhdf5 f64 read comparison excluded — clawhdf5's f32 datatype encoding differs from libhdf5's (known compatibility gap). f64 results are clawhdf5-only.
|
||||||
- Serial benchmarks. clawhdf5 uses Rayon for chunk compression when > 2 chunks; that parallelism is already reflected in the chunked write numbers.
|
- Serial benchmarks. clawhdf5 uses Rayon for chunk compression when > 2 chunks; that parallelism is already reflected in the chunked write numbers.
|
||||||
- clawhdf5 reads from `Vec<u8>` (zero-copy from mmap in production); libhdf5 reads from a temp file. This gives clawhdf5 a structural read advantage that reflects realistic API usage.
|
- clawhdf5 reads from `Vec<u8>` (zero-copy from mmap in production); libhdf5 reads from a temp file. This gives clawhdf5 a structural read advantage that reflects realistic API usage.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Independent Validation: tank (Ryzen 7 7800X3D), 2026-08-03
|
||||||
|
|
||||||
|
The `vs libhdf5 Summary` numbers above were re-run on a second, independently
|
||||||
|
administered machine (`tank`: AMD Ryzen 7 7800X3D, 8C/16T, Ubuntu 26.04, libhdf5
|
||||||
|
1.14.6 via `apt`) to confirm they reproduce off the original i7-12650H box, and to
|
||||||
|
add benchmark coverage for two claims that a documentation review found were not
|
||||||
|
traceable to any dated benchmark run (see git history around 2026-08-03 for context).
|
||||||
|
This section documents both.
|
||||||
|
|
||||||
|
### Reproduction of the vs-libhdf5 Summary table
|
||||||
|
|
||||||
|
| Workload | clawhdf5 (tank) | libhdf5 (tank) | Speedup (tank) | Speedup (i7-12650H, above) |
|
||||||
|
|----------|-----------------|-----------------|----------------|------------------------------|
|
||||||
|
| Sequential read, 1K f32 | 553 ns | 44.2 µs | **79.9×** | 71× |
|
||||||
|
| Sequential read, 100K f32 | 23.3 µs | 63.6 µs | **2.7×** | 3.0× |
|
||||||
|
| Sequential write, 100K f32 | 210 µs | 189 µs | **≈ tie** (clawhdf5 ~11% behind) | ≈ tie (clawhdf5 ~7% behind) |
|
||||||
|
| Chunked write deflate-6, 512×512 | 1.44 ms | 65.0 ms | **45.3×** | 38.4× |
|
||||||
|
| Attribute write, 128 attrs | 85.2 µs | 877 µs | **10.3×** | 10.6× |
|
||||||
|
| Group create, 64 groups | 130 µs | 1.37 ms | **10.6×** | 11.1× |
|
||||||
|
|
||||||
|
Five of six rows land within ~15% of the original i7-12650H figures — consistent
|
||||||
|
with normal cross-machine variance, not a methodology artifact. The chunked-write
|
||||||
|
row moved further (38.4× → 45.3×, +18%): tank's libhdf5 per-chunk write cost scales
|
||||||
|
worse relative to its own sequential-write throughput than on the i7, likely IPC/
|
||||||
|
memory-subsystem dependent. Both figures are real and dated; we report both rather
|
||||||
|
than picking one.
|
||||||
|
|
||||||
|
### New coverage: replacing the retracted "metadata parse / 308×" and "zero-copy mmap / 313 ns" claims
|
||||||
|
|
||||||
|
An earlier README revision cited `19 ns` vs `2,080 µs` (labeled, incorrectly, `308×`)
|
||||||
|
for "metadata parse," and `313 ns` for "zero-copy mmap" — neither figure traced to
|
||||||
|
any benchmark in this file. Both have been retracted from the README. In their
|
||||||
|
place, two new Criterion benchmarks were added
|
||||||
|
(`crates/clawhdf5-bench/benches/h5bench_meta.rs`,
|
||||||
|
`crates/clawhdf5-bench/benches/h5bench_read.rs`) and run on tank:
|
||||||
|
|
||||||
|
**`metadata_open_from_disk`** — opens a small file from disk (`std::fs::read` /
|
||||||
|
`hdf5::File::open`) and resolves one attribute. Both sides pay real OS I/O, unlike
|
||||||
|
the retracted claim.
|
||||||
|
|
||||||
|
| Operation | clawhdf5 | libhdf5 | Speedup |
|
||||||
|
|-----------|----------|---------|---------|
|
||||||
|
| Open file + read 1 attribute | 4.01 µs | 39.3 µs | **9.8×** |
|
||||||
|
|
||||||
|
**`metadata_parse_in_memory`** (clawhdf5-only) — times `File::from_bytes()` alone,
|
||||||
|
given bytes already resident in memory, i.e. header-parse cost with disk I/O
|
||||||
|
excluded. There is no fair libhdf5-side equivalent (its API has no "parse from an
|
||||||
|
in-memory buffer, skip the OS open" path), so this is reported standalone rather
|
||||||
|
than as a speedup multiple — this is the honest version of what the old `19 ns`
|
||||||
|
number was trying to claim.
|
||||||
|
|
||||||
|
| Operation | clawhdf5 (in-memory, no I/O) |
|
||||||
|
|-----------|------------------------------|
|
||||||
|
| Parse superblock + resolve 1 attribute | 549 ns |
|
||||||
|
|
||||||
|
**`read_zerocopy_mmap`** — opens via `MmapFile` and reads an f64 dataset through
|
||||||
|
`read_f64_zerocopy()`, summing every element to force the mapped pages to actually
|
||||||
|
fault in (returning only a slice length, as an earlier draft of this benchmark did,
|
||||||
|
would repeat the exact "measures nothing" mistake being fixed here).
|
||||||
|
|
||||||
|
| n (f64 elements) | clawhdf5 mmap (zerocopy, page-fault-forced) | clawhdf5 (`Vec<u8>` copy) | libhdf5 (disk open + copy) |
|
||||||
|
|-------------------|----------------------------------------------|----------------------------|------------------------------|
|
||||||
|
| 1,000 | 7.86 µs | 4.50 µs | 44.2 µs |
|
||||||
|
| 10,000 | 19.0 µs | 9.53 µs | 47.1 µs |
|
||||||
|
| 100,000 | 112 µs | 72.0 µs | 81.2 µs |
|
||||||
|
|
||||||
|
Honest result: at these sizes, forcing full materialization through the mmap path
|
||||||
|
is **not** faster than the plain `Vec<u8>` copy path — `mmap()`/page-fault overhead
|
||||||
|
per call outweighs the copy it avoids. This contradicts the retracted `313 ns`
|
||||||
|
claim outright and is a genuinely useful finding: `MmapFile`'s real advantage is
|
||||||
|
avoiding the allocation/copy for large files or sparse access patterns (lower peak
|
||||||
|
RSS, share pages across processes), not raw single-shot read latency at these
|
||||||
|
sizes. No README claim is made from this row; it's recorded here for the record
|
||||||
|
and to keep future readers from reintroducing the old number.
|
||||||
|
|
||||||
|
**Reproduce:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo bench -p clawhdf5-bench --features libhdf5-compare --bench h5bench_meta -- metadata_open_from_disk
|
||||||
|
cargo bench -p clawhdf5-bench --features libhdf5-compare --bench h5bench_meta -- metadata_parse_in_memory
|
||||||
|
cargo bench -p clawhdf5-bench --features libhdf5-compare --bench h5bench_read -- read_zerocopy_mmap
|
||||||
|
```
|
||||||
|
|||||||
@@ -10,8 +10,13 @@
|
|||||||
|
|
||||||
ClawHDF5 is a pure-Rust HDF5 implementation combined with a research-grade agent memory engine. It gives AI agents persistent, searchable, cryptographically verifiable memory — all stored in a single portable file.
|
ClawHDF5 is a pure-Rust HDF5 implementation combined with a research-grade agent memory engine. It gives AI agents persistent, searchable, cryptographically verifiable memory — all stored in a single portable file.
|
||||||
|
|
||||||
|
> **Two things live here:**
|
||||||
|
> - **A general-purpose, pure-Rust HDF5 library** — zero C dependencies, NetCDF-4 support, SIMD/GPU acceleration. See the **[Crate Map](#crate-map)** and **[BENCHMARKS.md](BENCHMARKS.md)** for the libhdf5 head-to-head numbers.
|
||||||
|
> - **An agent memory layer built on top of it** — vector search, knowledge graph, hippocampal-style consolidation, in `clawhdf5-agent`.
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo add clawhdf5-agent --features agent
|
cargo add clawhdf5 # core HDF5 read/write, no agent layer
|
||||||
|
cargo add clawhdf5-agent --features agent # + agent memory layer
|
||||||
```
|
```
|
||||||
|
|
||||||
> **New here?** Start with the **[Quickstart Guide](docs/QUICKSTART.md)** · See **[Use Cases](docs/USE_CASES.md)** · Read **[Benchmarks](BENCHMARKS.md)**
|
> **New here?** Start with the **[Quickstart Guide](docs/QUICKSTART.md)** · See **[Use Cases](docs/USE_CASES.md)** · Read **[Benchmarks](BENCHMARKS.md)**
|
||||||
@@ -37,7 +42,21 @@ Every AI agent needs memory. Today that means scattered Markdown files, SQLite d
|
|||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
Benchmarked on Intel i7-12650H (10C/16T), 384-dim embeddings, Criterion.rs.
|
Vector search and agent-memory operations below are benchmarked on Intel i7-12650H (10C/16T), 384-dim embeddings, Criterion.rs. The HDF5 Core I/O table immediately below is from a separate, independently reproduced run (see its own hardware note).
|
||||||
|
|
||||||
|
### HDF5 Core I/O (vs libhdf5 1.14.6)
|
||||||
|
|
||||||
|
*Benchmark numbers are being validated in collaboration with engineers from the HDF5 Group to confirm methodology and reproducibility.*
|
||||||
|
|
||||||
|
Figures below are from an independent reproduction run on a second machine (AMD Ryzen 7 7800X3D, 2026-08-03). Full methodology, the original i7-12650H run, and two additional benchmarks added to close prior coverage gaps (an I/O-inclusive metadata-open comparison and an honest zero-copy-mmap measurement) are in [BENCHMARKS.md § Independent Validation](BENCHMARKS.md#independent-validation-tank-ryzen-7-7800x3d-2026-08-03).
|
||||||
|
|
||||||
|
| Operation | ClawhDF5 | libhdf5 | Speedup |
|
||||||
|
|-----------|----------|---------|---------|
|
||||||
|
| Attribute write (128 attrs) | 85.2 µs | 877 µs | **10.3×** |
|
||||||
|
| Group create (64 groups) | 130 µs | 1.37 ms | **10.6×** |
|
||||||
|
| Chunked write, deflate-6 (512×512 f32) | 1.44 ms | 65.0 ms | **45.3×** |
|
||||||
|
| Sequential read (100K f32) | 23.3 µs | 63.6 µs | **2.7×** |
|
||||||
|
| Sequential write (100K f32) | 210 µs | 189 µs | **≈ tie** |
|
||||||
|
|
||||||
### Vector Search
|
### Vector Search
|
||||||
|
|
||||||
@@ -60,15 +79,6 @@ Benchmarked on Intel i7-12650H (10C/16T), 384-dim embeddings, Criterion.rs.
|
|||||||
| Memory write (WAL) | **18 µs** | per record (group-commit append; HDF5 batched at flush) |
|
| Memory write (WAL) | **18 µs** | per record (group-commit append; HDF5 batched at flush) |
|
||||||
| Importance gate | **61 ns** | per record |
|
| Importance gate | **61 ns** | per record |
|
||||||
|
|
||||||
### HDF5 Core I/O (vs h5py/C HDF5)
|
|
||||||
|
|
||||||
| Operation | ClawhDF5 | h5py (C) | Speedup |
|
|
||||||
|-----------|----------|----------|---------|
|
|
||||||
| Metadata parse | 19 ns | 2,080 µs | **308×** |
|
|
||||||
| Write 1M f64 | 0.82 ms | 1.60 ms | **2×** |
|
|
||||||
| Read 1M f64 | 0.28 ms | 0.65 ms | **2.3×** |
|
|
||||||
| Zero-copy mmap | 313 ns | N/A | — |
|
|
||||||
|
|
||||||
### Chunked Write Throughput (codec comparison)
|
### Chunked Write Throughput (codec comparison)
|
||||||
|
|
||||||
Measured with Criterion on f32 matrices. Auto-shuffle is applied before all compression codecs
|
Measured with Criterion on f32 matrices. Auto-shuffle is applied before all compression codecs
|
||||||
|
|||||||
@@ -220,6 +220,104 @@ fn bench_metadata_string_attrs(c: &mut Criterion) {
|
|||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Workload: metadata_open_from_disk
|
||||||
|
// Open a small pre-built file from disk and resolve one attribute. Both
|
||||||
|
// sides pay the OS open()/read() cost plus header-parse cost, so this is a
|
||||||
|
// fair, I/O-inclusive "open a file and touch its metadata" comparison — the
|
||||||
|
// honest version of the "metadata parse" claim this benchmark replaces.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn bench_metadata_open_from_disk(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("metadata_open_from_disk");
|
||||||
|
group.throughput(Throughput::Elements(1));
|
||||||
|
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let clawhdf5_path = tmp.path().join("open_clawhdf5.h5");
|
||||||
|
{
|
||||||
|
let mut fb = FileBuilder::new();
|
||||||
|
let ds = fb
|
||||||
|
.create_dataset("data")
|
||||||
|
.with_f64_data(&[1.0, 2.0, 3.0])
|
||||||
|
.with_shape(&[3]);
|
||||||
|
ds.set_attr("label", AttrValue::I64(42));
|
||||||
|
fb.write(&clawhdf5_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
group.bench_function("clawhdf5", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let raw = std::fs::read(&clawhdf5_path).unwrap();
|
||||||
|
let file = File::from_bytes(raw).unwrap();
|
||||||
|
let ds = file.dataset("data").unwrap();
|
||||||
|
ds.attrs().unwrap()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "libhdf5-compare")]
|
||||||
|
{
|
||||||
|
let libhdf5_path = tmp.path().join("open_libhdf5.h5");
|
||||||
|
{
|
||||||
|
let file = hdf5::File::create(&libhdf5_path).unwrap();
|
||||||
|
let ds = file
|
||||||
|
.new_dataset::<f64>()
|
||||||
|
.shape([3])
|
||||||
|
.create("data")
|
||||||
|
.unwrap();
|
||||||
|
ds.write(&[1.0f64, 2.0, 3.0]).unwrap();
|
||||||
|
ds.new_attr::<i64>()
|
||||||
|
.create("label")
|
||||||
|
.unwrap()
|
||||||
|
.write_scalar(&42i64)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
group.bench_function("libhdf5", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let file = hdf5::File::open(&libhdf5_path).unwrap();
|
||||||
|
let ds = file.dataset("data").unwrap();
|
||||||
|
let _: i64 = ds.attr("label").unwrap().read_scalar().unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Workload: metadata_parse_in_memory (clawhdf5-only)
|
||||||
|
// Times File::from_bytes() alone on bytes already resident in memory — i.e.
|
||||||
|
// the header-parse cost with disk I/O excluded. There is no fair libhdf5
|
||||||
|
// equivalent (its API has no "parse from an in-memory buffer" path that
|
||||||
|
// skips the OS open), so this is reported standalone, not as a speedup
|
||||||
|
// multiple against libhdf5. See metadata_open_from_disk above for the
|
||||||
|
// I/O-inclusive, directly comparable number.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn bench_metadata_parse_in_memory(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("metadata_parse_in_memory");
|
||||||
|
group.throughput(Throughput::Elements(1));
|
||||||
|
|
||||||
|
let bytes = {
|
||||||
|
let mut fb = FileBuilder::new();
|
||||||
|
let ds = fb
|
||||||
|
.create_dataset("data")
|
||||||
|
.with_f64_data(&[1.0, 2.0, 3.0])
|
||||||
|
.with_shape(&[3]);
|
||||||
|
ds.set_attr("label", AttrValue::I64(42));
|
||||||
|
fb.finish().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
group.bench_with_input(BenchmarkId::new("clawhdf5", "in_memory"), &bytes, |b, raw| {
|
||||||
|
b.iter(|| {
|
||||||
|
let file = File::from_bytes(raw.clone()).unwrap();
|
||||||
|
let ds = file.dataset("data").unwrap();
|
||||||
|
ds.attrs().unwrap()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
meta_benches,
|
meta_benches,
|
||||||
bench_metadata_attrs_write,
|
bench_metadata_attrs_write,
|
||||||
@@ -227,5 +325,7 @@ criterion_group!(
|
|||||||
bench_metadata_groups_create,
|
bench_metadata_groups_create,
|
||||||
bench_metadata_groups_traverse,
|
bench_metadata_groups_traverse,
|
||||||
bench_metadata_string_attrs,
|
bench_metadata_string_attrs,
|
||||||
|
bench_metadata_open_from_disk,
|
||||||
|
bench_metadata_parse_in_memory,
|
||||||
);
|
);
|
||||||
criterion_main!(meta_benches);
|
criterion_main!(meta_benches);
|
||||||
|
|||||||
@@ -209,6 +209,75 @@ fn bench_read_hyperslab(c: &mut Criterion) {
|
|||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Workload: read_zerocopy_mmap
|
||||||
|
// Opens a file from disk via `MmapFile` and reads an f64 dataset through
|
||||||
|
// `read_f64_zerocopy()`, which returns a slice directly into the mapped
|
||||||
|
// pages (no allocation, no copy). Compared against the regular
|
||||||
|
// std::fs::read + File::from_bytes path (which does copy), and — with
|
||||||
|
// libhdf5-compare — against libhdf5's own disk-backed open+read.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn bench_read_zerocopy_mmap(c: &mut Criterion) {
|
||||||
|
use clawhdf5::MmapFile;
|
||||||
|
|
||||||
|
let mut group = c.benchmark_group("read_zerocopy_mmap");
|
||||||
|
|
||||||
|
for &n in &[1_000usize, 10_000, 100_000] {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let path = tmp.path().join("mmap.h5");
|
||||||
|
let data: Vec<f64> = (0..n).map(|i| i as f64 * 0.001).collect();
|
||||||
|
let mut fb = FileBuilder::new();
|
||||||
|
fb.create_dataset("data")
|
||||||
|
.with_f64_data(&data)
|
||||||
|
.with_shape(&[n as u64]);
|
||||||
|
fb.write(&path).unwrap();
|
||||||
|
|
||||||
|
group.throughput(Throughput::Bytes((n * size_of::<f64>()) as u64));
|
||||||
|
|
||||||
|
group.bench_with_input(BenchmarkId::new("clawhdf5_mmap_zerocopy", n), &path, |b, p| {
|
||||||
|
b.iter(|| {
|
||||||
|
let file = MmapFile::open(p).unwrap();
|
||||||
|
let ds = file.dataset("data").unwrap();
|
||||||
|
let slice = ds.read_f64_zerocopy().unwrap();
|
||||||
|
// Sum every element to force the mapped pages to actually be
|
||||||
|
// faulted in — returning just `.len()` would measure nothing
|
||||||
|
// but the mmap() syscall, repeating the exact "too-fast-to-
|
||||||
|
// be-real" mistake this benchmark exists to fix.
|
||||||
|
let sum: f64 = slice.map(|s| s.iter().sum()).unwrap_or(0.0);
|
||||||
|
criterion::black_box(sum)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group.bench_with_input(BenchmarkId::new("clawhdf5_copy", n), &path, |b, p| {
|
||||||
|
b.iter(|| {
|
||||||
|
let raw = std::fs::read(p).unwrap();
|
||||||
|
let file = File::from_bytes(raw).unwrap();
|
||||||
|
file.dataset("data").unwrap().read_f64().unwrap()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "libhdf5-compare")]
|
||||||
|
group.bench_with_input(BenchmarkId::new("libhdf5", n), &n, |b, &nn| {
|
||||||
|
let tmp2 = TempDir::new().unwrap();
|
||||||
|
let path2 = tmp2.path().join("mmap_libhdf5.h5");
|
||||||
|
let data2: Vec<f64> = (0..nn).map(|i| i as f64 * 0.001).collect();
|
||||||
|
{
|
||||||
|
let lf = hdf5::File::create(&path2).unwrap();
|
||||||
|
let lds = lf.new_dataset::<f64>().shape([nn]).create("data").unwrap();
|
||||||
|
lds.write(data2.as_slice()).unwrap();
|
||||||
|
}
|
||||||
|
b.iter(|| {
|
||||||
|
let file = hdf5::File::open(&path2).unwrap();
|
||||||
|
let ds = file.dataset("data").unwrap();
|
||||||
|
ds.read_raw::<f64>().unwrap()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
read_benches,
|
read_benches,
|
||||||
bench_read_sequential,
|
bench_read_sequential,
|
||||||
@@ -216,5 +285,6 @@ criterion_group!(
|
|||||||
bench_read_chunked_2d,
|
bench_read_chunked_2d,
|
||||||
bench_read_from_disk,
|
bench_read_from_disk,
|
||||||
bench_read_hyperslab,
|
bench_read_hyperslab,
|
||||||
|
bench_read_zerocopy_mmap,
|
||||||
);
|
);
|
||||||
criterion_main!(read_benches);
|
criterion_main!(read_benches);
|
||||||
|
|||||||
+2
-2
@@ -344,7 +344,7 @@ let data = temp.read_f64()?;
|
|||||||
|
|
||||||
### Performance
|
### Performance
|
||||||
|
|
||||||
ClawhDF5 is 2–300× faster than h5py/C HDF5 for common operations. See [BENCHMARKS.md](../BENCHMARKS.md) for details. The zero-copy mmap path reads 1M floats in 313 nanoseconds.
|
ClawhDF5 is 3–45× faster than libhdf5 for common operations (see [BENCHMARKS.md](../BENCHMARKS.md#vs-libhdf5-summary) for methodology and an independent second-machine reproduction).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -548,7 +548,7 @@ let final_results = confidence::reject_low_confidence(
|
|||||||
- Hierarchical groups (natural fit for entity/relation/session organization)
|
- Hierarchical groups (natural fit for entity/relation/session organization)
|
||||||
- Compression built in (zlib, lz4, zstd)
|
- Compression built in (zlib, lz4, zstd)
|
||||||
- Battle-tested format (30+ years in scientific computing)
|
- Battle-tested format (30+ years in scientific computing)
|
||||||
- Our implementation is pure Rust, 2–300× faster than C HDF5 for metadata ops
|
- Our implementation is pure Rust, 10–11× faster than libhdf5 for metadata ops (attribute writes, group creation) — see [BENCHMARKS.md](../BENCHMARKS.md#vs-libhdf5-summary)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user