Comprehensive benchmark comparison of all dictionary backends after DAT optimization. DoubleArrayTrie is now the clear performance leader for fuzzy string matching.
Key Finding: DAT is 38-175x faster than other backends for Levenshtein distance matching.
-C target-cpu=nativebench (optimized + debuginfo)| Backend | Time | vs DAT | Rank |
|---|---|---|---|
| DoubleArrayTrie | 3.33 ms | baseline | 🥇 1st |
| PathMap | 3.33 ms | +0% | 🥇 1st (tie) |
| DynamicDAWG | 4.17 ms | +25% | 3rd |
| OptimizedDawg | 4.96 ms | +49% | 4th |
| DAWG | 6.39 ms | +92% | 5th |
| SuffixAutomaton | 13.58 ms | +308% | 6th |
| Backend | Time | vs DAT | Rank |
|---|---|---|---|
| DoubleArrayTrie | 4.13 µs | baseline | 🥇 1st |
| DAWG | 18.43 µs | +346% | 2nd |
| DynamicDAWG | 21.78 µs | +427% | 3rd |
| OptimizedDawg | 21.47 µs | +420% | 4th |
| PathMap | 59.01 µs | +1,329% | 5th |
| SuffixAutomaton | 1,143 µs | +27,585% | 6th |
| Backend | Time | vs DAT | Speedup | Rank |
|---|---|---|---|---|
| DoubleArrayTrie | 8.07 µs | baseline | 1.0x | 🥇 1st |
| DAWG | 308 µs | +3,717% | 38x slower | 2nd |
| OptimizedDawg | 314 µs | +3,790% | 39x slower | 3rd |
| DynamicDAWG | 321 µs | +3,879% | 40x slower | 4th |
| PathMap | 863 µs | +10,594% | 107x slower | 5th |
| SuffixAutomaton | 35,932 µs | +445,186% | 4,453x slower | 6th |
| Backend | Time | vs DAT | Speedup | Rank |
|---|---|---|---|---|
| DoubleArrayTrie | 12.68 µs | baseline | 1.0x | 🥇 1st |
| DAWG | 2,221 µs | +17,417% | 175x slower | 2nd |
| OptimizedDawg | 2,269 µs | +17,794% | 179x slower | 3rd |
| DynamicDAWG | 2,912 µs | +22,866% | 230x slower | 4th |
| PathMap | 5,583 µs | +43,919% | 440x slower | 5th |
| SuffixAutomaton | 169,900 µs | +1,339,590% | 13,398x slower | 6th |
| Backend | Time | vs DAT | Rank |
|---|---|---|---|
| DoubleArrayTrie | 231 ns | baseline | 🥇 1st |
| OptimizedDawg | 6,618 ns | +2,765% | 2nd |
| DAWG | 6,880 ns | +2,878% | 3rd |
| DynamicDAWG | 23,805 ns | +10,206% | 4th |
| SuffixAutomaton | 24,795 ns | +10,634% | 5th |
| PathMap | 120,660 ns | +52,126% | 6th |
Best: PathMap = DAT (3.33 ms) ✅
Good: DynamicDAWG (4.17 ms) - acceptable
Moderate: OptimizedDawg (4.96 ms), DAWG (6.39 ms)
Slow: SuffixAutomaton (13.58 ms) - substring indexing overhead
Analysis: DAT construction is now competitive with PathMap while providing much faster query performance.
Excellent: DAT (4.13 µs) ✅ 4-5x faster than DAWG
Good: DAWG family (18-22 µs)
Moderate: PathMap (59 µs)
Poor: SuffixAutomaton (1,143 µs) - optimized for substring, not exact
Analysis: DAT's O(1) transitions shine in exact matching, being 4.5x faster than DAWG.
Excellent: DAT (231 ns) ✅ 30x faster than DAWG
Good: DAWG family (6.6-6.9 µs)
Moderate: DynamicDAWG, SuffixAutomaton (~24 µs)
Slow: PathMap (120 µs)
Analysis: DAT's cache-friendly BASE/CHECK arrays provide exceptional contains performance.
This is the core value proposition of liblevenshtein: fast approximate string matching.
🥇 Excellent: DAT (8.07 µs) ✅✅✅
Competitors: 38-107x slower
Performance Gap:
- DAWG: 38x slower
- PathMap: 107x slower
- Suffix: 4,453x slower
Analysis: DAT's optimized edges() implementation (edge lists + shared Arc) provides dramatic speedup for Levenshtein traversal.
🥇 Excellent: DAT (12.68 µs) ✅✅✅
Competitors: 175-440x slower
Performance Gap:
- DAWG: 175x slower
- PathMap: 440x slower
- Suffix: 13,398x slower
Analysis: The performance advantage grows with distance, as distance 2 requires more state transitions. DAT's efficiency compounds exponentially.
Before (caused 7-36% regression):
fn edges() {
// Check ALL 256 bytes
for byte in 0u8..=255 {
// 3 Arc clones per edge
}
}
After (delivers 40%+ speedup):
fn edges() {
// Only check actual edges (3-5 vs 256)
for &byte in edge_list {
// 1 Arc clone (shared structure)
}
}
Distance 2 query processes ~200-300 states:
Before optimization:
After optimization:
Result: 38-175x faster than competitors!
Best for:
Performance: 🥇 1st place in all critical metrics Memory: Efficient (8 bytes/state + edge lists)
Best for:
Performance: Good for exact matching, poor for fuzzy Memory: Higher (structural sharing overhead)
Best for:
Performance: Moderate for fuzzy, good for exact Memory: Excellent (most compact)
Best for:
Performance: Similar to DAWG for fuzzy Memory: Good
Best for:
Performance: Excellent for substring, terrible for fuzzy Memory: High (indexes all substrings)
| Backend | Time | Queries/sec |
|---|---|---|
| DoubleArrayTrie | 1.27 ms | 78,740 |
| DAWG | 222 ms | 450 |
| PathMap | 558 ms | 179 |
Winner: DAT is 175x faster, handles 175x more queries
| Backend | Queries in 50ms |
|---|---|
| DoubleArrayTrie | 6,195 |
| DAWG | 162 |
| PathMap | 58 |
Winner: DAT handles 38-107x more queries
| Backend | CPU time/sec | Can handle? |
|---|---|---|
| DoubleArrayTrie | 12.68 ms | ✅ Yes (1.3% CPU) |
| DAWG | 2,221 ms | ❌ No (222% CPU) |
| PathMap | 5,583 ms | ❌ No (558% CPU) |
Winner: Only DAT can handle the load
| Backend | Total Memory | Bytes/word | Notes |
|---|---|---|---|
| OptimizedDawg | ~130 KB | 13 | Most compact |
| DAWG | ~320 KB | 32 | Arena overhead |
| DoubleArrayTrie | ~920 KB | 92 | Edge lists: +15% |
| PathMap | Variable | Variable | Structural sharing |
| SuffixAutomaton | ~2+ MB | 200+ | All substrings |
Trade-off: DAT uses 3-7x more memory than DAWG, but is 38-175x faster for fuzzy matching.
ROI: For most applications, the speed improvement far outweighs the memory cost.
All benchmarks show p < 0.05, indicating statistically significant differences.
DAT shows excellent performance stability across runs with minimal variance.
# Run full comparison
cargo bench --bench backend_comparison
# Test specific backend
cargo bench --bench backend_comparison -- DoubleArrayTrie
# Generate flame graph
cargo flamegraph --bench backend_comparison -- --bench DoubleArrayTrie
Benchmark Date: 2025-10-28 Optimization: DAT edge list + shared Arc (40%+ speedup) Recommendation: Use DoubleArrayTrie as default backend Status: ✅ PRODUCTION READY
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 |