-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Adaptive (runtime, stats-based) conjunct reordering for FilterExec #22698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
adriangb
wants to merge
27
commits into
apache:main
Choose a base branch
from
pydantic:lift-selectivity-stats
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
d69afa4
feat(physical-plan): adaptive conjunct reordering in FilterExec (comp…
adriangb ffe07f4
feat(physical-plan): pool adaptive measurements across partition streams
adriangb e2055c4
feat(physical-plan): use compact-once only in service of a reorder
adriangb 8beea6c
fix(physical-plan): address review feedback on adaptive filter
adriangb fdbbf14
fix(physical-plan): reset adaptive filter state on re-execution
adriangb b99a92a
fix(physical-plan): harden adaptive filter edge cases
adriangb 66e8900
test(sqllogictest): drive adaptive filter across the settle boundary
adriangb 1f0a68e
docs: tighten adaptive filter behavior claims
adriangb c9c7c36
refactor(physical-plan): FilterExec owns the enable gate; trace per-b…
adriangb 1bf6b20
refactor(physical-plan): drop redundant adaptive filter epoch
adriangb f76986b
feat(physical-plan): report adaptive filter reordering in FilterExec …
adriangb c94e22d
test(physical-plan): deterministic adaptive filter tests and flag-on …
adriangb 113b05c
docs(physical-plan): tighten adaptive filter documentation
adriangb 2b04353
refactor(physical-plan): evaluate the adopted order as a right-nested…
adriangb eeecd9d
refactor(physical-plan): measure conjuncts through BinaryExpr instead…
adriangb 8dac5ee
refactor(physical-plan): keep FilterExecMetrics::new signature; add w…
adriangb 19c8120
refactor(physical-plan): rank conjuncts by Velox's discards-per-time key
adriangb 15a5be3
perf(physical-plan): right-nest the kept written order; pool without …
adriangb 9dbdffe
refactor(physical-plan): trim adaptive filter to its essentials
adriangb 784e085
docs(physical-plan): drop stale order wording and fused test name
adriangb 1e4c62c
docs(physical-plan): cut comment bloat in adaptive filter
adriangb 571a388
refactor(physical-expr): share the AND pre-selection rule with its ca…
adriangb c54d917
fix(physical-plan): keep the written AND tree, and cost it as AND run…
adriangb df919f9
test(physical-plan): pin the 30% downstream weight directly
adriangb 0c0642e
fix(physical-expr): do not link private check_short_circuit from publ…
adriangb 8385e54
refactor(physical-expr): hide the shared AND pre-selection rule from …
adriangb 8f5dcd1
docs(physical-plan): describe the adaptive pool as node state, not pe…
adriangb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1164,7 +1164,67 @@ enum ShortCircuitStrategy { | |
| /// the side that cannot short-circuit the operator is rare: | ||
| /// - for `AND`, when the proportion of `true` is less than or equal to 0.2 | ||
| /// - for `OR`, when the proportion of `false` is less than or equal to 0.2 | ||
| const PRE_SELECTION_THRESHOLD: f32 = 0.2; | ||
| /// | ||
| /// Public only so that crates modelling this behaviour can share the | ||
| /// definition; not part of the API surface DataFusion advertises. | ||
| #[doc(hidden)] | ||
| pub const PRE_SELECTION_THRESHOLD: f32 = 0.2; | ||
|
|
||
| /// How much of the batch an `AND`'s right-hand side is evaluated on, given the | ||
| /// shape of its left-hand side's result. | ||
| /// | ||
| /// This is the observable consequence of `check_short_circuit` for `AND`, | ||
| /// exposed so that consumers modelling the cost of a conjunction share one | ||
| /// definition with the code that implements it. See [`and_rhs_evaluation`]. | ||
| /// | ||
| /// Public only so that crates modelling this behaviour can share the | ||
| /// definition; not part of the API surface DataFusion advertises. | ||
| #[doc(hidden)] | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub enum AndRhsEvaluation { | ||
| /// The left-hand side is `false` on every row, so the right-hand side is | ||
| /// not evaluated at all. | ||
| Skipped, | ||
| /// The left-hand side has no nulls and is `true` on few enough rows | ||
| /// ([`PRE_SELECTION_THRESHOLD`]), so the right-hand side is evaluated only | ||
| /// on the rows where it is `true`. | ||
| PreSelected, | ||
| /// The right-hand side is evaluated on the whole batch. This is the case | ||
| /// whenever the left-hand side produces a null, however selective it looks. | ||
| FullBatch, | ||
| } | ||
|
|
||
| /// What an `AND` does with its right-hand side, given its left-hand side's | ||
| /// `true` count, null count and length. | ||
| /// | ||
| /// `true_count` counts non-null `true`s; it is only consulted when | ||
| /// `null_count` is zero, where the two conventions coincide. | ||
| /// | ||
| /// `check_short_circuit` decides by this function, so a caller that models | ||
| /// conjunction cost cannot drift away from what evaluation actually does. | ||
| /// | ||
| /// Public only so that crates modelling this behaviour can share the | ||
| /// definition; not part of the API surface DataFusion advertises. | ||
| #[doc(hidden)] | ||
| pub fn and_rhs_evaluation( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, if we are making this pub just so it is cross-crate can we make this |
||
| true_count: usize, | ||
| null_count: usize, | ||
| len: usize, | ||
| ) -> AndRhsEvaluation { | ||
| // A null makes `AND` fall through to a plain full-batch evaluation: it can | ||
| // neither skip the right-hand side nor build a pre-selection mask from a | ||
| // left-hand side it cannot interpret row by row. | ||
| if null_count > 0 || len == 0 { | ||
| return AndRhsEvaluation::FullBatch; | ||
| } | ||
| if true_count == 0 { | ||
| return AndRhsEvaluation::Skipped; | ||
| } | ||
| if true_count < len && true_count as f32 / len as f32 <= PRE_SELECTION_THRESHOLD { | ||
| return AndRhsEvaluation::PreSelected; | ||
| } | ||
| AndRhsEvaluation::FullBatch | ||
| } | ||
|
|
||
| /// Checks if a logical operator (`AND`/`OR`) can short-circuit evaluation based on the left-hand side (lhs) result. | ||
| /// | ||
|
|
@@ -1202,35 +1262,41 @@ fn check_short_circuit(lhs: &ColumnarValue, op: &Operator) -> ShortCircuitStrate | |
| ColumnarValue::Array(array) => { | ||
| // Fast path for arrays - try to downcast to boolean array | ||
| if let Ok(bool_array) = as_boolean_array(array) { | ||
| // Arrays with nulls can't be short-circuited | ||
| if bool_array.null_count() > 0 { | ||
| return ShortCircuitStrategy::None; | ||
| } | ||
|
|
||
| let len = bool_array.len(); | ||
| if len == 0 { | ||
| return ShortCircuitStrategy::None; | ||
| } | ||
|
|
||
| let null_count = bool_array.null_count(); | ||
| let true_count = bool_array.values().count_set_bits(); | ||
| if is_and { | ||
| if true_count == 0 { | ||
| return ShortCircuitStrategy::ReturnLeft; | ||
| } | ||
|
|
||
| if true_count == len { | ||
| return ShortCircuitStrategy::ReturnRight; | ||
| } | ||
|
|
||
| if true_count as f32 / len as f32 <= PRE_SELECTION_THRESHOLD { | ||
| // Select rows where the LHS is true; rows where the LHS | ||
| // is false are false regardless of the RHS. | ||
| return ShortCircuitStrategy::PreSelection { | ||
| mask: bool_array.clone(), | ||
| fill_value: false, | ||
| }; | ||
| // Decided by `and_rhs_evaluation` so that consumers | ||
| // modelling this behaviour cannot drift away from it. | ||
| match and_rhs_evaluation(true_count, null_count, len) { | ||
| AndRhsEvaluation::Skipped => { | ||
| return ShortCircuitStrategy::ReturnLeft; | ||
| } | ||
| AndRhsEvaluation::PreSelected => { | ||
| // Select rows where the LHS is true; rows where the | ||
| // LHS is false are false regardless of the RHS. | ||
| return ShortCircuitStrategy::PreSelection { | ||
| mask: bool_array.clone(), | ||
| fill_value: false, | ||
| }; | ||
| } | ||
| // All true: the RHS alone decides. Otherwise fall | ||
| // through to a plain full-batch evaluation. | ||
| AndRhsEvaluation::FullBatch => { | ||
| if null_count == 0 && true_count == len { | ||
| return ShortCircuitStrategy::ReturnRight; | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| // Arrays with nulls can't be short-circuited | ||
| if null_count > 0 { | ||
| return ShortCircuitStrategy::None; | ||
| } | ||
| if true_count == len { | ||
| return ShortCircuitStrategy::ReturnLeft; | ||
| } | ||
|
|
||
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add doc hidden so this is not as strongly part of the public API?