Date: 2025-11-06 Purpose: Define performance validation strategy, success criteria, and benchmarking methodology for WallBreaker implementation.
This document outlines the comprehensive benchmarking plan to validate WallBreaker performance improvements. The primary goal is to confirm that Option B (Hybrid) achieves 3300x speedup over traditional approach for large error bounds and long patterns.
Key Success Criteria:
Validate WallBreaker Performance Gain
Ensure Correctness
Measure Resource Usage
Establish Baseline
We'll test across three dimensions:
Total Scenarios: 6 × 6 × 5 = 180 benchmark combinations
Purpose: Confirm WallBreaker doesn't hurt performance when not beneficial
| max_distance | Pattern Length | Dictionary Size | Expected Winner |
|---|---|---|---|
| 1 | 10 | 10K | Traditional |
| 2 | 20 | 100K | Traditional |
| 2 | 50 | 100K | WallBreaker (marginal) |
Success Criteria: WallBreaker no more than 20% slower than traditional
Purpose: Validate WallBreaker's core value proposition
| max_distance | Pattern Length | Dictionary Size | Expected Speedup |
|---|---|---|---|
| 4 | 20 | 10K | 5-10x |
| 4 | 50 | 100K | 50-100x |
| 8 | 50 | 100K | 500-1000x |
| 8 | 100 | 500K | 1000-2000x |
Success Criteria: Speedup ≥ 10x for all scenarios
Purpose: Demonstrate extreme wall effect mitigation
| max_distance | Pattern Length | Dictionary Size | Expected Speedup |
|---|---|---|---|
| 12 | 100 | 100K | 2000-3000x |
| 16 | 100 | 100K | 3000-5000x |
| 16 | 200 | 500K | 5000-10000x |
Success Criteria: Speedup ≥ 1000x for all scenarios, query time < 1ms
Purpose: Ensure robustness
| Scenario | Pattern | max_distance | Expected Behavior |
|---|---|---|---|
| Very short pattern | "ab" | 4 | Handle gracefully (may fall back to traditional) |
| Pattern = max_distance | "test" | 4 | Pattern split into 5 pieces, each very short |
| Empty dictionary | Any | Any | Return empty results quickly |
| Single-term dictionary | Any | Any | Correct result, low overhead |
| Very large dictionary | Any | 16 | 5M terms, still fast |
Success Criteria: No crashes, correct results, reasonable performance
Source: /usr/share/dict/words (or similar)
Characteristics:
Test Patterns:
Generation: Random strings from alphabet Characteristics:
Test Patterns:
Source: Short genomic sequences (k-mers) Characteristics:
Test Patterns:
Definition: Time from query start to completion (all results yielded)
Measurement:
let start = Instant::now();
let results: Vec<_> = fuzzy_search(&dict, pattern, max_distance).collect();
let duration = start.elapsed();
Reporting: Median, mean, min, max, stddev over 100 iterations
Target: < 0.2ms for 100-char pattern, max_distance=16, 100K dictionary
Definition: Traditional query time / WallBreaker query time
Calculation:
speedup = median_time_traditional / median_time_wallbreaker
Reporting: Speedup factor (e.g., "3300x faster")
Target: ≥ 1000x for max_distance ≥ 4, pattern length ≥ 50
Definition: Peak memory used during query execution
Measurement:
// Track allocations during query
let mem_before = get_memory_usage();
let results: Vec<_> = fuzzy_search(&dict, pattern, max_distance).collect();
let mem_after = get_memory_usage();
let mem_delta = mem_after - mem_before;
Reporting: MB used, percentage overhead vs baseline
Target: < 50% overhead vs traditional
Definition: Time to build dictionary structure (WallBreaker-enabled)
Measurement:
let start = Instant::now();
let dict = SuffixAutomaton::from_iter(terms);
let duration = start.elapsed();
Reporting: Median time over 10 iterations
Target: < 20% increase vs current SuffixAutomaton construction
Definition: Memory used by dictionary structure
Measurement: Size of SuffixAutomaton with parent links vs without
Reporting: MB used, percentage overhead
Target: < 30% increase (parent links are small)
Definition: Time to find all exact substring matches (phase 1 of WallBreaker)
Measurement:
let start = Instant::now();
let matches = dict.find_exact_substring(pattern_piece);
let duration = start.elapsed();
Reporting: Microseconds per search
Target: < 10% of total query time
Definition: Time for left + right extension from each substring match
Measurement: Profile left/right extension separately
Reporting: Microseconds per extension
Target: < 90% of total query time (bulk of work)
Definition: Percentage of results that match traditional approach
Measurement:
let trad_results: HashSet<_> = traditional_search(...).collect();
let wb_results: HashSet<_> = wallbreaker_search(...).collect();
let accuracy = (trad_results == wb_results) as f64 * 100.0;
Reporting: Percentage (should be 100%)
Target: 100% accuracy (no false positives, no false negatives)
Definition: Verify computed distances are correct
Measurement: For each result, compute true Levenshtein distance and compare
Reporting: Percentage of correct distances
Target: 100% accuracy
| Criterion | Target | Measurement | Priority |
|---|---|---|---|
| Correctness | 100% | Results match traditional | CRITICAL |
| Distance Accuracy | 100% | Distances correct | CRITICAL |
| No Crashes | 0 | All scenarios run | CRITICAL |
| Performance (b≥4) | ≥ 100x speedup | Median query time | CRITICAL |
If any Tier 1 criterion fails: Do not proceed to release. Fix issues first.
| Criterion | Target | Measurement | Priority |
|---|---|---|---|
| Query Time (b=16) | < 0.2ms | 100-char, 100K dict | HIGH |
| Speedup (b≥4) | ≥ 1000x | Pattern ≥50 chars | HIGH |
| Memory Overhead | < 50% | Query memory | HIGH |
| Construction Time | < 20% increase | Build time | HIGH |
| Small b Performance | ≤ 20% slower | b ≤ 2 | MEDIUM |
If Tier 2 criteria fail: Investigate optimization opportunities. May still release with caveats.
| Criterion | Target | Measurement | Priority |
|---|---|---|---|
| Query Time (b=16) | < 0.1ms | 100-char, 100K dict | LOW |
| Speedup (b≥8) | ≥ 3000x | Pattern ≥50 chars | LOW |
| Memory Overhead | < 30% | Query memory | LOW |
| Construction Time | < 10% increase | Build time | LOW |
If Tier 3 criteria met: Excellent! Document and celebrate. Not required for release.
Small Error Bound (b ≤ 2):
Medium Error Bound (b = 4-8):
Large Error Bound (b ≥ 12):
File: /benches/wallbreaker_comparison.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
use liblevenshtein::prelude::*;
fn benchmark_wallbreaker_vs_traditional(c: &mut Criterion) {
let mut group = c.benchmark_group("wallbreaker_comparison");
// Load dictionaries
let dict_10k = load_dictionary("data/dict_10k.txt");
let dict_100k = load_dictionary("data/dict_100k.txt");
let dict_1m = load_dictionary("data/dict_1m.txt");
// Test patterns
let patterns = vec![
("short", "test", 4),
("medium", "algorithm", 8),
("long", "extraordinarily", 16),
];
for (name, pattern, max_distance) in patterns {
// Traditional approach
group.bench_with_input(
BenchmarkId::new("traditional", name),
&pattern,
|b, pattern| {
b.iter(|| {
fuzzy_search(&dict_100k, black_box(pattern), max_distance)
.collect::<Vec<_>>()
});
},
);
// WallBreaker approach
group.bench_with_input(
BenchmarkId::new("wallbreaker", name),
&pattern,
|b, pattern| {
b.iter(|| {
fuzzy_search_wallbreaker(&dict_100k, black_box(pattern), max_distance)
.collect::<Vec<_>>()
});
},
);
}
group.finish();
}
criterion_group!(benches, benchmark_wallbreaker_vs_traditional);
criterion_main!(benches);
Location: /benches/data/
Files to Generate:
dict_1k.txt - 1,000 termsdict_10k.txt - 10,000 termsdict_100k.txt - 100,000 termsdict_500k.txt - 500,000 termsdict_1m.txt - 1,000,000 termsGeneration Script (/benches/generate_datasets.sh):
#!/bin/bash
# English words (100K)
head -n 100000 /usr/share/dict/words > benches/data/dict_100k.txt
head -n 10000 /usr/share/dict/words > benches/data/dict_10k.txt
head -n 1000 /usr/share/dict/words > benches/data/dict_1k.txt
# Synthetic (1M)
python3 benches/generate_synthetic.py 1000000 > benches/data/dict_1m.txt
python3 benches/generate_synthetic.py 500000 > benches/data/dict_500k.txt
File: /benches/generate_test_patterns.py
import random
import string
def generate_pattern(length: int, alphabet: str = string.ascii_lowercase) -> str:
return ''.join(random.choice(alphabet) for _ in range(length))
def inject_errors(pattern: str, num_errors: int) -> str:
"""Inject substitutions, insertions, deletions"""
pattern = list(pattern)
for _ in range(num_errors):
error_type = random.choice(['sub', 'ins', 'del'])
pos = random.randint(0, len(pattern) - 1)
if error_type == 'sub':
pattern[pos] = random.choice(string.ascii_lowercase)
elif error_type == 'ins':
pattern.insert(pos, random.choice(string.ascii_lowercase))
elif error_type == 'del' and len(pattern) > 1:
del pattern[pos]
return ''.join(pattern)
# Generate test patterns
for length in [10, 20, 50, 100, 200]:
pattern = generate_pattern(length)
for errors in [1, 2, 4, 8, 16]:
if errors < length // 2: # Reasonable error bound
fuzzy = inject_errors(pattern, errors)
print(f"{length},{errors},{pattern},{fuzzy}")
Full Benchmark Suite:
# Run all benchmarks
RUSTFLAGS="-C target-cpu=native" cargo bench --bench wallbreaker_comparison
# Run specific scenario
RUSTFLAGS="-C target-cpu=native" cargo bench --bench wallbreaker_comparison -- "b=16"
# Generate flamegraph
RUSTFLAGS="-C target-cpu=native" cargo flamegraph --bench wallbreaker_comparison
Quick Smoke Test:
# Run subset of scenarios for quick validation
RUSTFLAGS="-C target-cpu=native" cargo bench --bench wallbreaker_comparison -- "quick"
Step 1: Exact Match Test
#[test]
fn test_wallbreaker_correctness() {
let dict = load_test_dictionary();
let pattern = "algorithm";
let max_distance = 4;
let trad_results: HashSet<_> =
fuzzy_search(&dict, pattern, max_distance).collect();
let wb_results: HashSet<_> =
fuzzy_search_wallbreaker(&dict, pattern, max_distance).collect();
assert_eq!(trad_results, wb_results, "Results must match exactly");
}
Step 2: Distance Verification
#[test]
fn test_distance_accuracy() {
let dict = load_test_dictionary();
let pattern = "test";
let max_distance = 2;
for result in fuzzy_search_wallbreaker(&dict, pattern, max_distance) {
let actual_distance = levenshtein_distance(pattern, &result);
assert!(actual_distance <= max_distance, "Distance must be within bound");
}
}
Step 3: Edge Case Testing
#[test]
fn test_edge_cases() {
let dict = load_test_dictionary();
// Empty pattern
let results: Vec<_> = fuzzy_search_wallbreaker(&dict, "", 0).collect();
assert!(results.is_empty() || results == vec![""]);
// Pattern longer than any term
let results: Vec<_> = fuzzy_search_wallbreaker(&dict, "x".repeat(1000), 5).collect();
// Should not crash, results depend on dictionary
// max_distance = 0 (exact match)
let results: Vec<_> = fuzzy_search_wallbreaker(&dict, "test", 0).collect();
assert!(results.is_empty() || results == vec!["test"]);
}
Step 1: Baseline Measurement
# Measure traditional approach performance
RUSTFLAGS="-C target-cpu=native" cargo bench --bench traditional_baseline
# Save results
cargo bench --bench traditional_baseline -- --save-baseline traditional
Step 2: WallBreaker Measurement
# Measure WallBreaker performance
RUSTFLAGS="-C target-cpu=native" cargo bench --bench wallbreaker_comparison
# Save results
cargo bench --bench wallbreaker_comparison -- --save-baseline wallbreaker
Step 3: Comparison
# Compare baselines
cargo bench -- --baseline traditional --baseline wallbreaker
Using Valgrind (massif):
# Profile memory usage
valgrind --tool=massif ./target/release/deps/wallbreaker_comparison-*
# Visualize
ms_print massif.out.* > memory_profile.txt
Using Custom Memory Tracking:
use std::alloc::{GlobalAlloc, Layout, System};
struct TrackingAllocator;
static mut ALLOCATED: usize = 0;
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
ALLOCATED += layout.size();
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
ALLOCATED -= layout.size();
System.dealloc(ptr, layout)
}
}
#[global_allocator]
static GLOBAL: TrackingAllocator = TrackingAllocator;
Primary Benchmark Machine:
Reason for Selection: Representative of modern development machines, matches hardware mentioned in context.
Low-End (verify performance on constrained hardware):
High-End (verify scalability):
Rust Configuration:
[profile.bench]
opt-level = 3
lto = "fat"
codegen-units = 1
Environment:
export RUSTFLAGS="-C target-cpu=native"
export CARGO_PROFILE_BENCH_DEBUG=false
System Preparation:
nice -n -20 for high priority# WallBreaker Benchmark Results
**Date**: YYYY-MM-DD
**Hardware**: [CPU model, RAM, OS]
**Rust Version**: [version]
**Build**: RUSTFLAGS="-C target-cpu=native" cargo bench
---
## Summary
| Metric | Target | Actual | Status |
|--------|--------|--------|--------|
| Query Time (b=16, 100 chars) | < 0.2ms | [measured latency] | pass/fail |
| Speedup (b>=4, pattern>=50) | >= 1000x | [measured speedup] | pass/fail |
| Memory Overhead | < 50% | [measured overhead] | pass/fail |
| Construction Time Increase | < 20% | [measured increase] | pass/fail |
| Correctness | 100% | 100% | ✅ |
---
## Detailed Results
### Small Error Bound (b ≤ 2)
| Pattern | max_distance | Traditional | WallBreaker | Speedup | Winner |
|---------|--------------|-------------|-------------|---------|--------|
| "test" (4 chars) | 2 | [measure] | [measure] | [measure] | Traditional |
| "algorithm" (9 chars) | 2 | [measure] | [measure] | [measure] | Traditional |
**Analysis**: Fill in the measured small-error-bound delta; slower WallBreaker behavior is expected here.
---
### Medium Error Bound (b = 4-8)
| Pattern | max_distance | Traditional | WallBreaker | Speedup |
|---------|--------------|-------------|-------------|---------|
| "algorithm" | 4 | [measure] | [measure] | [measure] |
| "extraordinary" (50+ chars) | 8 | [measure] | [measure] | [measure] |
**Analysis**: Fill in the measured speedup and compare it against the 100x target.
---
### Large Error Bound (b ≥ 12)
| Pattern | max_distance | Traditional | WallBreaker | Speedup |
|---------|--------------|-------------|-------------|---------|
| "extraordinarily" (100 chars) | 16 | [measure] | [measure] | [measure] |
**Analysis**: Fill in the measured speedup and compare it against the 1000x target.
---
## Memory Profile
| Phase | Traditional | WallBreaker | Overhead |
|-------|-------------|-------------|----------|
| Construction | [measure] | [measure] | [measure] |
| Query | [measure] | [measure] | [measure] |
---
## Recommendations
[Based on results, any optimization suggestions or notes]
Generate Performance Charts:
import matplotlib.pyplot as plt
# Speedup vs Error Bound
error_bounds = [1, 2, 4, 8, 12, 16]
speedups = [0.8, 0.9, 100, 500, 2000, 3300] # Example data
plt.plot(error_bounds, speedups, marker='o')
plt.axhline(y=1, color='r', linestyle='--', label='Break-even')
plt.xlabel('Error Bound (max_distance)')
plt.ylabel('Speedup (x)')
plt.title('WallBreaker Speedup vs Error Bound')
plt.yscale('log')
plt.legend()
plt.savefig('speedup_vs_error_bound.png')
Generate Comparison Table:
# Generate markdown table from benchmark results
cargo bench -- --output-format bencher | python3 scripts/parse_benchmarks.py
GitHub Actions (.github/workflows/benchmark.yml):
name: Benchmark
on:
push:
branches: [master, feature/wallbreaker]
pull_request:
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run benchmarks
run: |
RUSTFLAGS="-C target-cpu=native" cargo bench --bench wallbreaker_comparison
- name: Compare with baseline
run: |
cargo bench -- --baseline master
Threshold: Fail if performance degrades by >10%
# Compare current with baseline
cargo bench -- --baseline master > results.txt
# Parse and check for regressions
python3 scripts/check_regression.py results.txt
Before running benchmarks:
During benchmarking:
After benchmarking:
# Full benchmark suite
RUSTFLAGS="-C target-cpu=native" cargo bench
# Specific benchmark
cargo bench --bench wallbreaker_comparison -- "b=16"
# With flamegraph
cargo flamegraph --bench wallbreaker_comparison
# Save baseline
cargo bench -- --save-baseline my-baseline
# Compare baselines
cargo bench -- --baseline baseline1 --baseline baseline2
# Generate report
cargo bench | tee benchmark_results.txt
Document Status: ✅ Complete Last Updated: 2025-11-06 Next Steps: Implement benchmarks during Phase 3 (Testing & Integration)
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 |