Repository navigation
branch-4.2: [improvement](be) Speed up dense unscored inverted index conjunctions (#69172) - #69239
Merged
Merged
Conversation
…apache#69172) ### What problem does this PR solve? Issue Number: N/A Related PR: apache#68758 Problem Summary: apache#68758 runs an unscored AND of terms on one field, such as MATCH_ALL, as a chain: the rarest term's rows are listed into an array and the other terms narrow them into another. On dense terms this was slower than the bitmap intersection it replaced on V2 segments, and its arrays grew with the segment (review comment M2 on apache#68758): 1. A term held by every row cannot remove any row, but the chain still read its postings. For `MATCH_ALL 'http status'`, where both terms are held by every row, it copied every row id twice and rebuilt the result bitmap from the array: 14.4 ms on a 2M-row V2 segment, about 10 times what it took before apache#68758. 2. A term left alone, such as `get` in `get http` once `http` is skipped, was listed into an array of all its rows before the result bitmap was built from it. For a term in 90% of 2M rows that cost about 1,200 extra page faults per query. 3. Intersecting a block through a bit set tested each candidate with a branch. For two unrelated terms each in half the rows (`alpha beta`), the branch mispredicted about half the time, and the intersection took 30% of the scan thread's cycles. 4. The arrays held up to 8 bytes per row of the segment in each scanner. This PR: - skips a term whose document frequency equals the source's document count, so a conjunction whose terms are all held by every row returns every row, or every candidate, without reading postings; - lists a lone term's blocks straight into the result bitmap, a dense block as a range; - intersects a block through its bit set without a branch per candidate: every candidate is written and kept only when its bit is set; - streams a conjunction of several terms on a source that reads on demand, such as a V2 segment: the cheapest term lists at most 2^16 of its rows at a time, the others narrow them, and the rows left go straight into the result, so no array holds more than a batch. The other terms keep their current block from one batch to the next, so each block is still decoded once. A batching source, such as SNII, keeps the chain, which reads each term's blocks in one round. Scored queries keep their path, and results are unchanged. Index filter time of MATCH_ALL on a 2M-row V2 segment, as the median ratio of 8 paired runs on fresh backends; each column is a separate comparison and lower is faster: | Query | Rows holding each term | vs apache#68758 | vs before apache#68758 | |---|---|---|---| | `http status` | 100%, 100% | 0.01 | 0.10 | | `rare http` | 0.05%, 100% | 0.31 | 0.10 | | `sparse http` | 0.5%, 100% | 0.23 | 0.31 | | `get http` | 90%, 100% | 0.60 | 0.95 | | `alpha beta` | 50%, 50% | 0.56 | 0.72 | In the comparison with apache#68758, the `http status` filter drops from 16.9 ms to 0.15 ms and its end-to-end time to 0.63, and the end-to-end time of `alpha beta` to 0.81. SNII segments take the same paths except the stream; there these queries take 0.02 to 0.83 of apache#68758 and 0.02 to 0.93 of their time before apache#68758. Seven more shapes, from `rare alpha` and `sparse alpha` to `alpha beta get` and the empty `get post`, take 0.20 to 0.89 of their time before apache#68758 on V2 and 0.57 to 1.00 on SNII. Two-term MATCH_ALL queries on short text take 0.86 to 1.01 of apache#68758, and the 18 queries used to accept apache#68758 (MATCH_PHRASE, MATCH_ANY and SEARCH phrases on V2 and SNII) stay between 0.97 and 1.03 of it, every 95% interval including 1. On V2 the stream holds at most two arrays of 2^16 rows per scanner, and it cuts the page faults per query of `alpha beta` from 458 to 192 and those of `get alpha` from 484 to 47. Its scan threads run within 1% of the chain's instructions, and a cheapest term of at most 2^16 rows is listed in one batch, as the chain lists it. ### Release note None ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [x] Unit Test - [x] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - [ ] This is a refactor/code format and no logic has been changed. - [ ] Previous test can cover this change. - [ ] No code files have been changed. - [ ] Other reason <!-- Add your reason? --> Unit test: `ListedTermsTest.AConjunctionSkipsATermHeldByEveryRow` and `ListedTermsTest.AConjunctionOfTermsHeldByEveryRowHoldsEveryRow` fail before the first commit, because the chain read the term held by every row, and pass after it. `ListedTermsTest.AConjunctionOnAStreamingSourceStreamsItsTerms` replaces `AConjunctionOnAStreamingSourceChainsItsTermsToo`, which recorded that such a source chained its terms, and fails before the last commit. `ListedTermsTest.ALoneTermListsItsRowsOrThoseAmongTheCandidates`, `CursorChainedPostings.IntersectionHandlesRandomDensities` and the `CursorChainedPostings.AStream*` tests cover the lone-term listing, the intersection and the stream; each fails when the code it covers is broken on purpose. Manual test: a 2M-row corpus with terms held by 100%, 90%, 50%, 10%, 0.5% and 0.05% of the rows was loaded into a V2 table and an SNII table. Each of 8 pairs started two fresh backends, one per build, swapping which backend ran which build on alternate pairs; each query ran 4 warm-up and 16 measured times on each build, alternating between the builds. Every result matched aggregates computed independently from the corpus. Scan-thread CPU time, cycles, instructions and page faults were sampled with `perf stat` over 200 runs of each query on each build. - Behavior changed: - [x] No. - [ ] Yes. <!-- Explain the behavior change --> - Does this need documentation? - [x] No. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into --> (cherry picked from commit f937d95)
Contributor
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
Member
Author
|
run buildall |
Contributor
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
yiguolei
approved these changes
Oct 19, 2026
Contributor
|
PR approved by anyone and no changes requested. |
Contributor
|
PR approved by at least one committer and no changes requested. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What problem does this PR solve?
Issue Number: N/A
Related PR: #69172, #69206
Problem Summary: Backport #69172 after #69206 introduced the shared inverted-index query engine to branch-4.2. Dense unscored conjunctions now skip terms held by every row, write a lone term directly to the result bitmap, use branch-free block intersection, and bound streaming-source batches. Query results are unchanged.
Source PR: #69172
Source squash commit:
f937d950d45313f20864d287df453e1b90f5c615(cherry-picked with-x).Release note
None
Check List (For Author)
test_match_format_paritypassed on an isolated branch-4.2 FE/BE cluster after rebasing ontoecd322ad31193cda19cf4f3b08e0f3f9d12dd67c(1 suite, 0 failures).ListedTermsTest.*:CursorChainedPostings.*passed (41 tests in 2 suites); all 9 source-added tests were listed and executed. No sanitizer report../build.sh --be -j64and./build.sh --fe -j8passed; isolated backend reportedAlive=true, andSELECT 1succeeded.Validation
build-support/check-format.sh: PASS.origin/branch-4.2: PASS.build-support/run-clang-tidy.sh; the branch's BE build and format gates passed.Hunk audit
The source squash patch ID matches the final source PR head. The cherry-pick was clean, and every changed file has a byte-identical postimage to the source commit.
be/src/storage/index/inverted/query/engine/boolean_query/listed_terms.cppbe/src/storage/index/inverted/query/exec/cursor_chained_postings.cppbe/src/storage/index/inverted/query/exec/cursor_chained_postings.hbe/test/storage/index/inverted/query/engine/boolean_query/listed_terms_test.cppbe/test/storage/index/inverted/query/exec/cursor_chained_postings_test.cppgit range-diffshows only the-xcherry-pick provenance line; the code patch is unchanged.