Date: 2025-11-12 Status: Evaluation phase - determining if pursuit is justified Baseline: H1 + H3 (current optimized state)
Recommendation: ❌ DO NOT PURSUE H4-H6 at this time
Rationale:
Decision: Reject H4-H6 for the current architecture unless production profiling reveals new bottlenecks
Based on crossover analysis and profiling:
Analysis: Limited remaining headroom for further optimization without fundamental algorithm changes.
For very small sets (≤8 pairs), SIMD parallel comparison using AVX2 instructions could outperform linear scan by checking multiple pairs simultaneously.
Optimistic: 1-3% improvement for 1-4 pair sets Realistic: <1% improvement (likely within noise)
Current Performance (H3 linear scan):
SIMD Potential:
Break-Even Analysis:
Linear scan cost: N × 1.0ns per pair (sequential comparison)
SIMD setup cost: ~5-10ns (load, shuffle, prepare mask)
SIMD comparison: ~1-2ns (parallel compare + extract result)
For 1 pair: Linear = 1.0ns, SIMD = 7-12ns → Linear wins by 7-12×
For 4 pairs: Linear = 4.0ns, SIMD = 7-12ns → Linear still wins
For 8 pairs: Linear = 8.0ns, SIMD = 7-12ns → SIMD competitive at best
Conclusion: SIMD overhead likely negates any parallel benefit for small sets.
Required Changes:
cfg(target_feature = "avx2"))std::arch::x86_64)simd feature already exists but unused)Code Complexity: +150-200 LOC Maintenance Burden: High (platform-specific, CPU feature detection) Testing Burden: Multiple code paths (AVX2, SSE4.1, fallback)
| Factor | Weight | Score (1-5) | Weighted | Notes |
|---|---|---|---|---|
| Expected Improvement | 40% | 1 | 0.4 | <1% realistic |
| Code Complexity | 20% | 1 | 0.2 | +150-200 LOC, platform-specific |
| Maintenance | 20% | 1 | 0.2 | High burden (multiple paths) |
| Testing | 10% | 2 | 0.2 | Need multi-platform CI |
| Portability | 10% | 1 | 0.1 | x86-64 only, feature detection |
| Total | 100% | — | 1.1/5 | REJECT |
Threshold for pursuit: 3.0/5 H4 score: 1.1/5 → Strong reject
❌ DO NOT PURSUE H4
Rationale:
Compile-time perfect hash function for fixed presets eliminates runtime hash computation entirely, potentially faster than even const array initialization.
Optimistic: 1-2% improvement for preset initialization Realistic: <0.5% improvement (likely within noise)
Current Performance (H1 const arrays):
Perfect Hashing Potential:
Best-Case Savings:
phonetic_basic: 14 pairs × 3ns = 42ns saved → 158ns - 42ns = 116ns (26% gain)
keyboard_qwerty: 68 pairs × 3ns = 204ns saved → 495ns - 204ns = 291ns (41% gain)
Reality Check:
allow_byte() vs allow())Realistic Savings: <10% of initialization time → <1% end-to-end
Required Tools:
Code Complexity: +100-150 LOC (build.rs + codegen) Build-Time Cost: Increased compile time Maintenance Burden: Medium (build-time dependencies)
Problem: Perfect hash only optimizes lookup, not initialization
// Current (H1):
const PHONETIC_PAIRS: &[(u8, u8)] = &[...];
SubstitutionSet::from_pairs(PHONETIC_PAIRS); // 158ns
// Perfect hash (H5):
const PHONETIC_HASH: PerfectHashMap<(u8, u8), ()> = ...;
// Still need to convert to SubstitutionSet → same initialization cost!
Conclusion: Perfect hashing doesn't address the right bottleneck. Initialization cost dominates, not lookup during init.
| Factor | Weight | Score (1-5) | Weighted | Notes |
|---|---|---|---|---|
| Expected Improvement | 40% | 1 | 0.4 | <0.5% realistic |
| Code Complexity | 20% | 2 | 0.4 | Build-time codegen |
| Maintenance | 20% | 2 | 0.4 | phf dependency |
| Build-Time Cost | 10% | 2 | 0.2 | Increased compile time |
| Applicability | 10% | 2 | 0.2 | Presets only (not user sets) |
| Total | 100% | — | 1.6/5 | REJECT |
Threshold for pursuit: 3.0/5 H5 score: 1.6/5 → Reject
❌ DO NOT PURSUE H5
Rationale:
Specialized hasher for (u8, u8) pairs could reduce collisions and improve performance over general-purpose FxHasher.
Optimistic: 1-2% improvement for hash-based lookups Realistic: <0.5% improvement (FxHasher already excellent for small keys)
Current Performance (FxHashSet with FxHasher):
Custom Hasher Potential:
Best-Case Savings:
FxHash computation: ~3-5ns
Custom hash: ~1-2ns
Savings: ~2-3ns per lookup
For typical query (100 lookups):
Current: 100 × 5ns = 500ns
Optimized: 100 × 3ns = 300ns
Savings: 200ns (40% of hash time)
But hash time is only part of total query time!
Total query time: ~10-50µs
Hash savings: 200ns = 0.2-2% of total
Conclusion: Hash computation is tiny fraction of total query time.
Required Changes:
struct PairHasher;
impl Hasher for PairHasher {
fn write(&mut self, bytes: &[u8]) { /* custom logic */ }
fn write_u16(&mut self, i: u16) { /* optimized for (u8, u8) */ }
fn finish(&self) -> u64 { /* return hash */ }
}
impl BuildHasher for PairHasherBuilder {
type Hasher = PairHasher;
fn build_hasher(&self) -> Self::Hasher { /* ... */ }
}
// Usage:
FxHashSet::with_hasher(PairHasherBuilder);
Code Complexity: +50-100 LOC Maintenance Burden: Medium (custom hasher logic) Testing Burden: Need collision testing, distribution analysis
FxHasher Characteristics:
Custom Hasher Challenges:
| Factor | Weight | Score (1-5) | Weighted | Notes |
|---|---|---|---|---|
| Expected Improvement | 40% | 1 | 0.4 | <0.5% end-to-end |
| Code Complexity | 20% | 3 | 0.6 | +50-100 LOC, but straightforward |
| Maintenance | 20% | 2 | 0.4 | Custom hasher logic |
| Testing | 10% | 2 | 0.2 | Need collision analysis |
| Risk | 10% | 2 | 0.2 | Worse collisions possible |
| Total | 100% | — | 1.8/5 | REJECT |
Threshold for pursuit: 3.0/5 H6 score: 1.8/5 → Reject
❌ DO NOT PURSUE H6
Rationale:
| Hypothesis | Expected Gain | Complexity | Score | Decision |
|---|---|---|---|---|
| H1 (Const Arrays) | 15-28% | Low (+50 LOC) | 4.5/5 | ✅ ACCEPTED |
| H2 (Bitmap) | -400% init | Medium (+200 LOC) | 1.0/5 | ❌ REJECTED |
| H3 (Hybrid) | 9-46% | Medium (+70 LOC) | 4.75/5 | ✅ ACCEPTED |
| H4 (SIMD) | <1% | High (+150-200 LOC) | 1.1/5 | ❌ REJECT |
| H5 (Perfect Hash) | <0.5% | Medium (+100-150 LOC) | 1.6/5 | ❌ REJECT |
| H6 (Custom Hasher) | <0.5% | Medium (+50-100 LOC) | 1.8/5 | ❌ REJECT |
Acceptance Threshold: 3.0/5
Results:
Excellent Current State
Diminishing Returns
Cost/Benefit
Better Alternatives
H4-H6 should ONLY be reconsidered if:
Production profiling reveals:
Substantial resources available:
New algorithmic insights:
Current Status: None of these conditions are met → REJECT FOR CURRENT ARCHITECTURE
Production Monitoring
Iterative Improvement
Algorithmic Innovation
The SubstitutionSet optimization project has successfully delivered two production-ready optimizations (H1, H3) with 9-46% improvements. Further micro-optimizations (H4-H6) are not justified given:
Final Decision: ❌ REJECT H4-H6 FOR CURRENT ARCHITECTURE
Status: Optimization project COMPLETE - Mission accomplished with H1 + H3 ✅
Document Version: 1.0 Last Updated: 2025-11-12 Author: Claude Code (Anthropic AI Assistant) Decision: H4-H6 rejected for the current architecture; optimization project closed successfully
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 |