Skip to content

MB-72489: omit postings-count read during automaton candidate collection#436

Merged
Thejas-bhat merged 3 commits into
masterfrom
perf-fuzzy-regexp-opts
Jul 24, 2026
Merged

MB-72489: omit postings-count read during automaton candidate collection#436
Thejas-bhat merged 3 commits into
masterfrom
perf-fuzzy-regexp-opts

Conversation

@capemox

@capemox capemox commented Jul 17, 2026

Copy link
Copy Markdown
Member

Split out from #435 per review — the automaton (fuzzy/regexp) candidate-collection optimization, as its own PR. No functional/API changes.

Change

DictionaryIterator.Next() unconditionally deserialized each visited term's postings list (roaring FromBuffer + GetCardinality) to populate DictEntry.Count. Candidate-term collectors in bleve (fuzzy/regexp/wildcard) discard that count, so it was O(candidates) wasted bitmap deserializations per query. The omitCount field existed to suppress this but was never wired up.

New AutomatonIteratorOmitCount returns an iterator that leaves Count at zero and skips the postings read; the existing AutomatonIterator is unchanged (both share a helper). Consumed opt-in by bleve's fuzzy/regexp field-dict constructors (blevesearch/bleve#2382).

Benchmark

BenchmarkFuzzyIterator (1000-term / 3000-doc segment, fuzziness-2 automaton matching 280 multi-hit candidates), Apple M4 Pro, count=10 via benchstat, with-count vs omit-count:

sec/op B/op allocs/op
−35.5% −57.4% −47.7% (595→311)

All p=0.000, n=10. TestAutomatonIteratorOmitCountSemantics (added) asserts the omit iterator yields identical terms/order/edit-distances — only the discarded count differs.

DictionaryIterator.Next() unconditionally read each visited term's postings
list (roaring bitmap deserialization via FromBuffer + GetCardinality) to
populate DictEntry.Count. Candidate-term collectors in bleve (fuzzy, regexp,
wildcard) discard that count, so the read was pure waste — O(candidates)
bitmap deserializations per query.

The omitCount field already existed on DictionaryIterator to suppress this,
but nothing ever set it. Wire it up: add AutomatonIteratorOmitCount alongside
the existing AutomatonIterator (both delegate to a shared helper), returning
an iterator that leaves DictEntry.Count at zero and skips the postings read.
AutomatonIterator's behavior is unchanged.

Benchmark (BenchmarkFuzzyIterator, added here): 1000-term / 3000-doc segment,
fuzziness-2 automaton matching 280 multi-hit candidates, Apple M4 Pro,
count=10 via benchstat, with-count vs omit-count:

  sec/op      34.27µ -> 22.10µ   -35.5%  (p=0.000)
  B/op        11.61Ki -> 4.95Ki  -57.4%  (p=0.000)
  allocs/op   595 -> 311         -47.7%  (p=0.000)

TestAutomatonIteratorOmitCountSemantics (added here) asserts the omit iterator
visits exactly the same terms, in the same order, with the same edit distances
as the regular iterator — the only difference being the discarded count.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Thejas-bhat Thejas-bhat changed the title perf: omit postings-count read during automaton candidate collection MB-72489: omit postings-count read during automaton candidate collection Jul 23, 2026
@Thejas-bhat
Thejas-bhat marked this pull request as ready for review July 23, 2026 17:49

@steveyen steveyen left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Thejas-bhat
Thejas-bhat merged commit 96521ab into master Jul 24, 2026
9 checks passed
@Thejas-bhat
Thejas-bhat deleted the perf-fuzzy-regexp-opts branch July 24, 2026 16:37
Thejas-bhat added a commit to blevesearch/bleve that referenced this pull request Jul 24, 2026
Split out from #2381 per review — the fuzzy/regexp candidate-collection
optimizations, as their own PR. No functional/API changes.

### Changes

- **Trim fuzzy candidate-collection overhead**
(`search/searcher/search_fuzzy.go`): skip the per-candidate dedup map
when the field has no synonyms (the dictionary iterator already yields
each term once; the map only exists to de-dup synonym terms), replace
the O(n²) `prefixTerm` rune-concat with a zero-alloc slice, and hoist
repeated `ctx.Value` lookups.
- **Omit postings-count read for fuzzy/regexp candidate collection**
(`index/scorch/snapshot_index*.go`): fuzzy/regexp collectors use only
the term + edit distance and discard `DictEntry.Count`, yet the segment
iterator read each candidate's postings list to compute it. A new opt-in
`AutomatonIteratorOmitCount` path skips that read, wired via a graceful
type-assertion fallback. `FieldDict`/`FieldDictPrefix`/`FieldDictRange`
still populate counts.

**Depends on** blevesearch/zapx#436 — the omit-count optimization
activates once the `zapx/v17` dependency is bumped to a release that
includes `AutomatonIteratorOmitCount`. Until then the wiring falls back
to the count-reading path (the wiring test skips), so this is safe to
merge independently.

### Benchmark

`BenchmarkFuzzyCandidateCollection` (scorch index, 1000 terms / 3000
docs, fuzziness-2 query matching 280 candidates), original → both
commits, Apple M4 Pro, count=12 via benchstat:

| | sec/op | B/op | allocs/op |
|---|---|---|---|
| micro-opts | −5.9% | −3.8% | −1.3% (988→975) |
| omit-count wiring | −9.7% | −1.1% | −29.1% (975→691) |
| **combined** | **−15.0%** | **−4.8%** | **−30.1%** (988→691) |

All p=0.000, n=12. Verified with the full fuzzy + synonym search suites
and `-race`.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Thejas-bhat <thejas.orkombu@couchbase.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants