Date Started: 2025-11-12 Objective: Systematically identify and optimize performance bottlenecks in SubstitutionSet/SubstitutionSetChar operations Hardware: Intel Xeon E5-2699 v3 @ 2.30GHz (36 cores, 72 threads), 252 GB RAM Baseline Commit: e5a32a0 (docs: Update universal-levenshtein README with SmallVec implementation status)
This optimization follows rigorous scientific methodology:
Created Benchmarks:
benches/substitution_set_microbench.rs - Micro-benchmarks for isolated operations
contains() with varying set sizes (1-500 pairs)allow_byte, allow)benches/substitution_integration_bench.rs - Real-world query scenarios
Test Configuration:
RUSTFLAGS="-C target-cpu=native"--features rand (for micro-benchmarks)Micro-Benchmark Run:
RUSTFLAGS="-C target-cpu=native" taskset -c 0 cargo bench \
--bench substitution_set_microbench --features rand \
2>&1 | tee /tmp/substitution_set_baseline.txt
Integration Benchmark Run:
RUSTFLAGS="-C target-cpu=native" taskset -c 1 cargo bench \
--bench substitution_integration_bench \
2>&1 | tee /tmp/substitution_integration_baseline.txt
Status: RUNNING (started 2025-11-12)
(To be filled after benchmarks complete)
(Planned)
# Contains() hot path profiling
RUSTFLAGS="-C target-cpu=native" taskset -c 0 cargo flamegraph \
--bench substitution_set_microbench --features rand \
-- --bench "single_lookup/hit"
(Planned)
taskset -c 0 perf stat -e cycles,instructions,cache-references,cache-misses,branches,branch-misses \
-- cargo bench --bench substitution_set_microbench --features rand \
-- --bench "single_lookup"
(To be filled after profiling)
Hypothesis: Preset substitution sets (phonetic, keyboard, etc.) have known, fixed contents at compile time. Using const arrays would eliminate runtime hash computations and char-to-byte conversions during initialization.
Expected Impact: 5-15% improvement for preset initialization
Actual Impact: 15-28% improvement (exceeded expectations!)
Implementation:
substitution_set_const.rs with const array-based presetsallow_byte() vs allow())Results (2025-11-12):
Decision: KEEP - All presets exceed 2% threshold significantly
Documentation: See 02-hypothesis1-const-arrays.md for full analysis
Status: ✅ COMPLETED - Approved for production integration
Hypothesis: For byte-level (ASCII) substitutions, a 128×128 bit matrix (2KB) would provide O(1) lookup with excellent cache locality, outperforming hash-based approaches for small-to-medium sized sets.
Expected Impact: 3-10% improvement for ASCII contains() calls
Actual Impact:
Implementation:
substitution_set_bitmap.rs with 128×128 bit array (2KB)bitmap[a * 16 + b / 8] & (1 << (b % 8))Results (2025-11-12):
Lookup Performance (EXCELLENT):
Initialization Performance (CATASTROPHIC):
Break-Even Analysis:
Decision: ❌ REJECT - Initialization cost (4-13×) outweighs lookup benefits (2.4×)
Why Rejected:
Documentation: See 03-hypothesis2-bitmap.md for full analysis
Status: ❌ COMPLETED - Rejected, experimental code to be removed
Hypothesis: Small substitution sets (≤4 pairs) would benefit from linear scan over hash lookup overhead. Hybrid approach: Vec for ≤4 pairs, FxHashSet for >4.
Expected Impact: 2-5% improvement for small custom sets
Actual Impact: 9-46% improvement for small sets (exceeded expectations!)
Implementation:
Small(Vec<(u8, u8)>) vs Large(FxHashSet<(u8, u8)>)Results (2025-11-12):
Micro-benchmarks (by set size):
Integration benchmarks (real-world):
Memory Benefits:
Decision: ✅ KEEP - Strong improvements for target use case, zero critical regressions
Key Finding: Micro-benchmark regressions (sizes 4, 7, 10) do NOT translate to integration test regressions. Real-world usage shows universal improvement.
Documentation: See 06-hypothesis3-hybrid.md for full analysis
Status: ✅ COMPLETED - Production-ready, ready for deployment
Hypothesis: For very small sets (≤8 pairs), SIMD parallel comparison using AVX2 instructions could outperform linear scan by checking multiple pairs simultaneously.
Expected Impact: 1-3% improvement for tiny sets
Actual Impact: <1% realistic (SIMD setup overhead negates benefits)
Evaluation Results (2025-11-12):
Break-Even Analysis:
Cost/Benefit Score: 1.1/5 (threshold: 3.0/5)
Decision: ❌ REJECT - Setup overhead exceeds parallel benefit, adds platform-specific complexity (+150-200 LOC) for <1% gain
Documentation: See 08-h4-h6-evaluation.md for full analysis
Status: ❌ EVALUATED AND REJECTED - Deferred indefinitely
Hypothesis: Compile-time perfect hash function for fixed presets eliminates runtime hash computation entirely, potentially faster than const array initialization.
Expected Impact: 1-2% improvement for preset initialization
Actual Impact: <0.5% realistic (doesn't address initialization bottleneck)
Evaluation Results (2025-11-12):
Critical Issue: Perfect hashing optimizes lookup, not initialization. Memory allocation dominates initialization cost, not hash computation.
Cost/Benefit Score: 1.6/5 (threshold: 3.0/5)
Decision: ❌ REJECT - Doesn't address the right bottleneck (memory allocation), adds build-time complexity (+100-150 LOC) for <0.5% gain
Documentation: See 08-h4-h6-evaluation.md for full analysis
Status: ❌ EVALUATED AND REJECTED - Deferred indefinitely
Hypothesis: Specialized hasher for (u8, u8) pairs could reduce collisions and improve performance over general-purpose FxHasher.
Expected Impact: 1-2% improvement for hash-based lookups
Actual Impact: <0.5% end-to-end (hash time is tiny fraction of total query time)
Evaluation Results (2025-11-12):
Best-Case Analysis:
Cost/Benefit Score: 1.8/5 (threshold: 3.0/5)
Decision: ❌ REJECT - Hard to beat production-proven FxHasher, risk of worse collisions, <0.5% end-to-end gain not justified
Documentation: See 08-h4-h6-evaluation.md for full analysis
Status: ❌ EVALUATED AND REJECTED - Deferred indefinitely
Completed: 2025-11-12
Test Suite: All 509 tests passing ✅
Integration Benchmarks (H3 results):
Comprehensive Cost/Benefit Analysis: 2025-11-12
Decision: Defer indefinitely due to insufficient ROI (<3% combined gains for high complexity)
Project Status: ✅ SUCCESSFULLY COMPLETED (2025-11-12)
Performance Improvements:
Code Additions:
Environment:
Reproduction Commands:
# Clone repository
git clone https://github.com/vinary-tree/liblevenshtein-rust
cd liblevenshtein-rust
git checkout <commit-hash>
# Run benchmarks
RUSTFLAGS="-C target-cpu=native" taskset -c 0 \
cargo bench --bench substitution_set_microbench --features rand
RUSTFLAGS="-C target-cpu=native" taskset -c 1 \
cargo bench --bench substitution_integration_bench
Last Updated: 2025-11-12 Experiment Owner: Claude Code (Anthropic AI Assistant)
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 |