--release profile with LTOAll planned phases completed:
| Phase | Description | Status |
|---|---|---|
| 7.1 | Add storage fields to PersistentARTrieInner | Complete |
| 7.2 | Add open() and create() constructors with recovery | Complete |
| 7.3 | Implement lazy SwizzledPtr resolution in lookup | Complete |
| 7.4 | Implement node serialization on insert | Complete |
| 7.5 | Wire prefetcher for DFS traversal | Complete |
| 8.1-8.4 | Integrate WAL with operations | Complete |
| 9.1-9.2 | Implement startup recovery | Complete |
| 10.1-10.4 | Complete ART node operations | Complete |
| 11 | Performance benchmarking | Complete |
Measured using Criterion.rs with 10 samples per benchmark:
| Dictionary Size | Time | Throughput |
|---|---|---|
| 100 terms | 79.2 µs | 1.26 M elements/sec |
| 500 terms | 251.4 µs | 1.99 M elements/sec |
| 1000 terms | 335.1 µs | 2.98 M elements/sec |
Analysis: Insert throughput increases with dictionary size due to amortized allocation overhead. At 1000 terms, the dictionary achieves approximately 3 million insertions per second with full durability (sync to disk).
| Dictionary Size | Time | Throughput |
|---|---|---|
| 100 terms | 119.7 µs | 836 K elements/sec |
| 500 terms | 447.9 µs | 1.12 M elements/sec |
| 1000 terms | 673.1 µs | 1.49 M elements/sec |
Analysis: Recovery is approximately 1.5-2x slower than initial creation, which is expected due to WAL replay overhead. Recovery throughput scales sub-linearly with dictionary size.
| Dictionary Size | Time | Throughput |
|---|---|---|
| 100 terms | 1.72 µs | 58.1 M elements/sec |
| 500 terms | 1.72 µs | 290.6 M elements/sec |
| 1000 terms | 1.70 µs | 588.0 M elements/sec |
Analysis: Checkpoint is nearly constant time (~1.7 µs) regardless of dictionary size, as it only marks a point in the WAL rather than copying data.
Additional benchmarks comparing PersistentARTrie against other dictionary types:
| Dictionary Type | 100 terms | 1000 terms | 5000 terms |
|---|---|---|---|
| PersistentARTrie | ~50 µs | ~400 µs | ~2.5 ms |
| DynamicDawg | ~20 µs | ~200 µs | ~1.2 ms |
| DoubleArrayTrie | ~15 µs | ~180 µs | ~1.0 ms |
Analysis: PersistentARTrie is approximately 2-2.5x slower than in-memory-only structures due to WAL logging overhead, which is expected for durability guarantees.
| Dictionary Type | 100 terms | 1000 terms | 5000 terms |
|---|---|---|---|
| PersistentARTrie | ~15 µs | ~18 µs | ~22 µs |
| DynamicDawg | ~12 µs | ~14 µs | ~18 µs |
| DoubleArrayTrie | ~8 µs | ~10 µs | ~12 µs |
Analysis: Lookup performance is comparable across dictionary types, with PersistentARTrie showing ~20-30% overhead due to lock acquisition.
Durability vs Performance Trade-off: PersistentARTrie achieves ~3M inserts/sec with full durability, suitable for high-throughput applications requiring crash recovery.
Sub-millisecond Recovery: Recovery of 1000-term dictionaries completes in under 1ms, enabling fast restarts.
Constant-time Checkpoints: Checkpoint operations are 𝒪(1), allowing frequent checkpoint markers without performance impact.
Competitive Lookup: Despite persistence overhead, lookup performance remains competitive with in-memory structures.
All 219 tests pass, including:
test result: ok. 219 passed; 0 failed; 197 ignored; 0 measured; 0 filtered out
Value Serialization: The DictionaryValue trait does not include serialization bounds, so values cannot be persisted to disk. Values are stored as Vec<u8> internally but cannot be converted back to generic V without adding serde bounds.
DiskRef Resolution: Lazy loading from disk (ChildNode::DiskRef) is implemented but not fully tested under memory pressure scenarios.
Group Commit: The GroupCommit mechanism exists but is not integrated into the main insert path.
serde bounds to DictionaryValue for full value persistenceCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |