Tuning & Tips
The engine is built once and cheap to query repeatedly. A few habits keep searches fast and results clean.
Filter early
- Raise the threshold. A higher similarity threshold prunes weak partial matches before they expand — it improves both quality and speed. It is the single most effective knob.
- Tighten edit limits.
FuzzyLimitsper pattern, when you know the expected error characteristics, cuts the explored state space directly.
Shape the cost model to your domain
- Use the similarity table for look-alike single characters (OCR glyphs, homoglyphs) so those substitutions are cheap and everything else stays expensive.
- Use
FuzzyPenaltiesto make whole edit types cheaper or pricier. - Use the weakest-link floor when a single bad character should disqualify a match regardless of length.
Pick the right entry point
- Prefer a
.non_overlapping()/.non_overlapping_unique()SearchOptionsover resolving overlaps yourself. - Use
SearchOptions::default()(unordered, overlaps kept) as the fastest primitive when you want to build a custom ranking/selection pipeline.
Guard against pathological input
Combining a high edit budget with a low threshold is the classic slow case. Add
auto_beam (keeps common cases exact) or an explicit beam_width when limits are high
and thresholds low, especially for untrusted input.
Reach for the specialized paths when they fit
- Streaming for large or incremental inputs (constant memory), and its parallel forms to use all cores on CPU-bound scans.
- The pre-filter for large, sparse inputs where most of the text can’t match — a big speedup with identical results, and a safe fallback when it doesn’t apply.
Memory footprint
The compiled automaton is immutable and shared by reference (&FuzzyAhoCorasick) — build it once and
query it from as many threads as you like without copying it. Each search allocates only bounded,
transient per-call state (the BFS frontier, a visited-set for state dedup, and a best-match-per-span
map). That state scales with the haystack length and match density, not with the automaton’s
pattern count, so per-query memory stays flat even for automata with millions of patterns. This makes
the engine cheap to fan out across many concurrent queries: the resident footprint is the one shared
automaton plus a small, bounded slice per in-flight search.
Size expectations
A single search call keeps grapheme positions as u32, so one haystack
must contain at most u32::MAX grapheme clusters (roughly a 4 GiB ASCII input). A larger haystack
returns Err(SearchError::HaystackTooLarge) rather than silently truncating positions to wrong
offsets — for inputs that big (or unbounded streams), use the streaming API,
which windows the input and reports absolute u64 offsets.
Measuring
Benchmark with your real patterns and representative text — throughput depends heavily on pattern
count/length, edit budget, threshold, and match density. The repository ships
Criterion benchmarks (cargo bench) and the
bitap_prototype / replace_bench examples as starting points.