Comprehensive benchmarking of all 5 dictionary backends reveals distinct performance trade-offs:
Best for Static Dictionaries: OptimizedDawg and DAWG show excellent balance of construction speed, memory efficiency, and query performance.
Best for Dynamic Dictionaries: DynamicDAWG offers good performance with modification support.
Best for In-Memory Speed: PathMap (baseline) fastest for small datasets but poor scaling.
Best for Substring Matching: SuffixAutomaton (specialized use case).
| Backend | Time (ms) | Relative | Notes |
|---|---|---|---|
| PathMap | 3.07 | 1.0x (baseline) | Fastest construction |
| DynamicDAWG | 4.02 | 1.3x | Good dynamic performance |
| OptimizedDawg | 5.59 | 1.8x | Winner for static dictionaries |
| DAWG | 6.23 | 2.0x | Slower than OptimizedDawg |
| SuffixAutomaton | 13.13 | 4.3x | Slowest (suffix tree construction) |
Analysis:
| Backend | Time (µs) | Relative | Speedup |
|---|---|---|---|
| DAWG | 18.9 | 1.0x | Fastest |
| OptimizedDawg | 21.2 | 1.1x | Very competitive |
| DynamicDAWG | 26.0 | 1.4x | Still fast |
| PathMap | 69.7 | 3.7x | Slower despite being in-memory |
| SuffixAutomaton | 1,223.8 | 64.8x | Much slower (substring overhead) |
Analysis:
| Backend | Time (µs) | Relative | Speedup |
|---|---|---|---|
| DAWG | 291 | 1.0x | Fastest |
| DynamicDAWG | 328 | 1.1x | Very close |
| OptimizedDawg | 333 | 1.1x | Competitive |
| PathMap | 887 | 3.0x | 3x slower |
| SuffixAutomaton | 37,087 | 127x | Extremely slow |
Analysis:
| Backend | Time (µs) | Relative | Speedup |
|---|---|---|---|
| DAWG | 2,120 | 1.0x | Fastest |
| OptimizedDawg | 2,341 | 1.1x | Very close |
| DynamicDAWG | 2,384 | 1.1x | Competitive |
| PathMap | 5,550 | 2.6x | 2.6x slower |
| SuffixAutomaton | 183,810 | 86.7x | Extremely slow |
Analysis:
| Backend | Time (µs) | Relative | Speedup |
|---|---|---|---|
| OptimizedDawg | 6.43 | 1.0x | Fastest! |
| DAWG | 6.54 | 1.02x | Virtually identical |
| DynamicDAWG | 24.0 | 3.7x | Slower (check overhead) |
| SuffixAutomaton | 25.1 | 3.9x | Similar to DynamicDAWG |
| PathMap | 115.8 | 18.0x | Much slower |
Analysis:
| Metric | PathMap | DAWG | OptimizedDawg | DynamicDAWG | SuffixAutomaton |
|---|---|---|---|---|---|
| Construction (10k words) | 3.07ms | 6.23ms | 5.59ms ✓ | 4.02ms | 13.13ms |
| Exact Match | 69.7µs | 18.9µs ✓ | 21.2µs | 26.0µs | 1,223µs |
| Distance 1 | 887µs | 291µs ✓ | 333µs | 328µs | 37,087µs |
| Distance 2 | 5,550µs | 2,120µs ✓ | 2,341µs | 2,384µs | 183,810µs |
| Contains (100) | 115.8µs | 6.54µs | 6.43µs ✓ | 24.0µs | 25.1µs |
| Dynamic Updates | ❌ | ❌ | ❌ | ✅ | ✅ |
| Memory Efficiency | ❌ Poor | ✅ Good | ✅✅ Best | ✅ Good | ⚠️ Variable |
✓ = Winner in category ✅ = Good ❌ = Not supported / Poor
Construction: 10.2% faster (5.59ms vs 6.23ms) Exact Matching: 12.5% slower (21.2µs vs 18.9µs) Distance 1: 14.3% slower (333µs vs 291µs) Distance 2: 10.4% slower (2,341µs vs 2,120µs) Contains: 1.7% FASTER (6.43µs vs 6.54µs)
Verdict: OptimizedDawg delivers on its promise:
PathMap is slower than expected:
Why? HashMap overhead and poor cache locality outweigh the benefits of uncompressed storage.
DynamicDAWG is remarkably competitive:
This makes it an excellent choice when modifications are needed.
SuffixAutomaton is specialized for substring matching:
Based on data structure sizes:
| Backend | Bytes/Node | Bytes/Edge | Total (10k words) | Notes |
|---|---|---|---|---|
| PathMap | ~64 | N/A | ~640 KB | HashMap + string data |
| DAWG | ~32 | ~24 | ~450 KB | Vec-based edges |
| OptimizedDawg | ~8 | ~5 | ~300 KB | Arena allocation |
| DynamicDAWG | ~40 | ~24 | ~500 KB | Dynamic structure overhead |
| SuffixAutomaton | ~48 | ~24 | ~600 KB | Suffix links + edges |
Estimated Memory Reduction: OptimizedDawg uses ~33% less memory than standard DAWG.
yada (Yet Another Double-Array)datrie (v1.0.0, Sept 2024)append(), load())search(), lookup(), contain()datrie crate ⚠️Pros:
Cons:
Verdict: Not recommended due to GPL license conflict.
Pros:
yada and datrie as referencesCons:
Verdict: Recommended - implement our own with:
Implement custom DAT backend (~900 lines, 4-5 hours):
Add varying dictionary sizes to benchmarks:
Benchmark DAT vs all backends:
Final documentation:
| Use Case | Best Backend | Runner-up | Avoid |
|---|---|---|---|
| Large static dictionary | OptimizedDawg | DAWG | PathMap |
| Small in-memory dict (<1k) | PathMap | OptimizedDawg | SuffixAutomaton |
| Dictionary with updates | DynamicDAWG | (Future: DAT) | DAWG |
| Substring matching | SuffixAutomaton | — | All others |
| Memory-constrained | OptimizedDawg | DAWG | PathMap |
| Construction speed priority | PathMap | DynamicDAWG | SuffixAutomaton |
| Query speed priority | DAWG | OptimizedDawg | PathMap |
| Balanced all-around | OptimizedDawg | DynamicDAWG | — |
OptimizedDawg successfully achieves its design goals:
Next Priority: Implement custom DAT backend to complete the comparison.
License Decision: Cannot use datrie (GPL-3.0) or yada (static-only). Must roll our own.
Current: 55k / 200k (27.5% used, 72.5% remaining)
Sufficient for:
Can 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 |