Date: 2025-11-18 23:20 System: Intel Xeon E5-2699 v3 @ 2.30GHz (taskset -c 0) Compiler: RUSTFLAGS="-C target-cpu=native", bench profile
Establish precise baseline measurements and formulate testable hypotheses for the observed performance degradation in large inputs.
| Input Size (phones) | Total Time (ns) | Std Dev | Time per Phone (ns) | Ratio vs Baseline |
|---|---|---|---|---|
| 5 | 1,132 | ±40 | 226 | 1.00× (baseline) |
| 10 | 2,677 | ±98 | 268 | 1.19× |
| 20 | 8,425 | ±309 | 421 | 1.86× |
| 50 | 42,997 | ±1,673 | 860 | 3.80× ⚠️ |
Expected Behavior: O(n) linear scaling → ~226 ns/phone across all sizes Actual Behavior: Superlinear degradation as input size increases
Degradation Curve:
5 phones: 226 ns/phone (baseline)
10 phones: 268 ns/phone (+19%)
20 phones: 421 ns/phone (+86%)
50 phones: 860 ns/phone (+280%) ⚠️
Critical Finding: Performance degrades by 280% at 50 phones compared to 5-phone baseline.
Scaling Characteristic:
Theory: Frequent Vec reallocations during apply_rule_at() and apply_rules_seq()
Evidence:
apply_rule_at() creates new Vec for every rule applicationCode Location: src/phonetic/application.rs:177-187
let mut result = Vec::with_capacity(s.len() + MAX_EXPANSION_FACTOR);
result.extend_from_slice(&s[..pos]);
result.extend_from_slice(&rule.replacement);
result.extend_from_slice(&s[(pos + rule.pattern.len())..]);
Expected Impact: O(n) allocations × reallocation cost → O(n²) behavior
Testable Prediction:
Vec::reserve() or alloc::realloc()Theory: Nested loops in pattern matching lead to O(n²) behavior
Evidence:
apply_rules_seq() iterates through all rules for each applicationfind_first_match() scans entire string for each ruleCode Location: src/phonetic/application.rs:203-227
loop {
for rule in rules { // O(r) rules
if let Some(pos) = find_first_match(rule, ¤t) { // O(n) scan
// Apply rule
}
}
}
Expected Impact: For each application, O(r × n) work → total O(f × r × n) where f = applications
Testable Prediction:
find_first_match() or pattern_matches_at()Theory: Large phonetic vectors exceed cache size, causing cache misses
Evidence:
Expected Impact: Minimal for sizes tested (50 phones << L1 cache size)
Testable Prediction:
perf stat should show low cache miss rate for 50-phone caseTheory: Frequent slice copying in apply_rule_at() creates overhead
Evidence:
&s[..pos] (prefix)&rule.replacement (replacement)&s[(pos + rule.pattern.len())..] (suffix)Expected Impact: O(n) copying per application → total O(f × n)
Testable Prediction:
extend_from_slice()Based on Likelihood and Expected Impact:
H1 (Vec Reallocation): HIGHEST PRIORITY
H2 (Quadratic Pattern Matching): HIGH PRIORITY
H4 (Slice Copying): MEDIUM PRIORITY
H3 (Cache Inefficiency): LOW PRIORITY
Code Analysis: Examine source code for H1 and H2
apply_rules_seq() and apply_rule_at()Targeted Profiling (only when ready to analyze immediately):
Perf Stat Analysis:
perf stat to check cache miss rates (H3)For each hypothesis:
Confirmed Issue: 3.80× performance degradation at 50 phones vs expected linear scaling
Leading Hypotheses:
Expected Root Cause: Combination of H1 and H2
Target Improvement: Reduce 50-phone time from 42,997 ns to ~11,300 ns (3.8× speedup)
Next Phase: Code analysis and targeted profiling to validate H1 and H2
Baseline Established: ✅ Hypotheses Formulated: ✅ Ready for Phase 2: ✅
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 |