Abhisheklearn12 commented on issue #25035:
URL: https://github.com/apache/datafusion/issues/25035#issuecomment-5669281208

   Sorry for the delayed follow-up, @adriangb, I was tied up with other work. I 
independently reproduced and traced the current evaluator at 
[`9082d6b10`](https://github.com/apache/datafusion/commit/9082d6b10c29b72d56bede3d8e353d9d61fde542).
   
   The association-sensitive slowdown is real. Accumulated-prefix selectivity 
is useful, but insufficient as the sole trigger. Evidence points to node-local 
state plus policy: binary parents discard compact state, while the 20% rule 
omits bytes copied and remaining work. My measurements reproduce the 
qualitative result; absolute timings and ratios differ. I have not selected 
A/B/C, implemented B/C, or reproduced the reported TPC-H, `predicate_eval`, 
prototype, or 510-SLT results.
   
   Benchmark: [immutable 
commit](https://github.com/Abhisheklearn12/datafusion/commit/290f57d24efe9cd3cb75bb78570cc68cd03cf988),
 SHA-256 `2f0338362241221bc8fbdf3f0cf96c913feb4307513ba167e80a0a6152fbd29c`. 
This benchmark commit is a reproducibility artifact only, not a proposed 
production change or PR.
   
   ### Reproduction and mechanism
   
   The target evaluates eight ordered SplitMix64-salted `Int32 > 0` conjuncts 
over 8,192 rows at roughly 70%, with 32 unreferenced `Int64` payload columns. 
Both use the same leaf definitions and order. Criterion used 1-second warmup, 
3-second measurement, and 40 samples on an Intel i7-11700F, Linux 6.1, rustc 
1.97.0, and Arrow 59.3.0. Criterion's slope point estimates were **172.681 µs** 
[95% CI: 172.151, 173.214] and **31.965 µs** [95% CI: 31.915, 32.017], a 
**5.40x** ratio. Three invocations gave 5.39x to 5.41x. Frequency was unpinned 
and targets sequential, so small differences are not meaningful.
   
   The mixed-RHS path is 
[`BinaryExpr::evaluate`](https://github.com/apache/datafusion/blob/9082d6b10c29b72d56bede3d8e353d9d61fde542/datafusion/physical-expr/src/expressions/binary.rs#L542-L611)
 -> 
[`check_short_circuit`](https://github.com/apache/datafusion/blob/9082d6b10c29b72d56bede3d8e353d9d61fde542/datafusion/physical-expr/src/expressions/binary.rs#L1188-L1272)
 -> Arrow 
[`filter_record_batch`](https://github.com/apache/arrow-rs/blob/59.3.0/arrow-select/src/filter.rs#L441-L463)
 -> RHS evaluation -> 
[`pre_selection_scatter`](https://github.com/apache/datafusion/blob/9082d6b10c29b72d56bede3d8e353d9d61fde542/datafusion/physical-expr/src/expressions/binary.rs#L1307-L1354).
 Each trigger filters the full batch and restores a full-length Boolean array 
because the selected RHS is mixed. The uniform-RHS shortcut does not apply, and 
compact state is not propagated upward.
   
   Exact prefix survivors: `5742 -> 3946 -> 2749 -> 1905 -> 1338 -> 889 -> 617 
-> 425`. At 8,192 rows, mixed non-null arrays with 1 to 1,638 true rows 
qualify, so the last three left-deep parents trigger. Right-deep LHS values are 
individual approximately 70%-passing leaves and do not. Arrow 
`filter_record_batch` filters every column, including unreferenced payload.
   
   ### Controlled experiments
   
   | Control | Left | Right | L/R |
   |---|---:|---:|---:|
   | k=2, p=.70, width=32 | 7.356 µs | 7.349 µs | 1.00x |
   | k=8, p=.70, width=0 | 73.062 µs | 31.878 µs | 2.29x |
   | k=8, p=.70, width=32 | 172.681 µs | 31.965 µs | 5.40x |
   | k=8, p=.70, width=64 | 282.648 µs | 31.899 µs | 8.86x |
   | k=8, p=.90, no firing | 31.900 µs | 31.872 µs | 1.00x |
   | k=16, p=.70, width=32 | 380.918 µs | 68.728 µs | 5.54x |
   | k=8, p=.70, regex suffix | 146.079 µs | 375.879 µs | 0.39x |
   
   Width amplifies only the preselecting path; the no-fire control removes the 
gap. Regex reverses it, making left-deep 2.57x faster. This strongly argues 
against disabling preselection globally or treating right-deep as the fix.
   
   These Samply stack counts are inclusive and may overlap:
   
   | Profile | Samples | `filter_record_batch` | `pre_selection_scatter` |
   |---|---:|---:|---:|
   | Left | 7,948 | 4,937 (62.12%) | 1,263 (15.89%) |
   | Right | 8,031 | 1 (0.01%) | 2 (0.02%) |
   | Left p=.90 | 7,898 | 4 (0.05%) | 0 |
   
   ####  Right-deep: `pre_selection_scatter`
   <img width="1455" height="486" alt="Image" 
src="https://github.com/user-attachments/assets/fc4b5d52-f47b-4acc-a258-7ce897f3886c";
 />
   
   #### Right-deep: `filter_record_batch`
   <img width="1454" height="497" alt="Image" 
src="https://github.com/user-attachments/assets/e6b2096a-c097-4dbf-b09c-e6e90ceb2900";
 />
   
   #### Left-deep: `filter_record_batch`
   <img width="1454" height="491" alt="Image" 
src="https://github.com/user-attachments/assets/294373f1-5802-46aa-821d-4301ca588bc6";
 />
   
   #### Left-deep: `pre_selection_scatter`
   <img width="1457" height="499" alt="Image" 
src="https://github.com/user-attachments/assets/5443fba4-7b4f-4632-93a9-6f216481235f";
 />
   
   
   ### Correctness constraints and next decision
   
   All 11 generated comparisons and 22 Criterion targets passed, alongside the 
targeted nullable/scalar association, short-circuit/scatter, 
physical-expression library (1,643 passed, 2 ignored), logical right-deep 
protobuf round-trip, and `simplify_inlist` tests. These validate current cases, 
not candidates.
   
   For Kleene value semantics, definite `FALSE` rows can retire, but `NULL` 
must remain active because `NULL AND FALSE = FALSE`. 
[`evaluate_selection`](https://github.com/apache/datafusion/blob/9082d6b10c29b72d56bede3d8e353d9d61fde542/datafusion/physical-expr-common/src/physical_expr.rs#L97-L180)
 guarantees that skipped rows cannot trigger errors. A changed active set can 
therefore alter rows evaluated, first observable error, and rows on which 
volatile expressions run.
   
   Git history points to [`4818966fa0` / 
#15694](https://github.com/apache/datafusion/commit/4818966fa0950a6a471d1ea9e4b8c5808878207e)
 as introducing partial-row filtering/scatter. Protobuf preserves an explicitly 
right-deep logical tree, while fully mergeable `InList` chains can simplify 
bottom-up. These findings narrow the serialization and `InList` objections to 
A, but A still changes global shape and does not address the evaluator’s state 
or cost policy.
   
   This motivates testing A as a control. B would carry one active-row mapping 
through the `AND` chain and restore full length once; C would gate compaction 
using its predicted benefit. I would test B, C, then B+C independently. Before 
prototyping, I would value alignment on:
   
   1. Is evaluator-local `AND` flattening the right initial boundary, 
preserving stored shape, leaf order, serialization, display, optimizer 
behavior, and public API?
   2. What observable contract must hold for fallible and volatile leaves: rows 
evaluated, evaluation count, and/or first observable error?
   3. Should B and C remain independently switchable, and which cheap signals 
may C use: survivor ratio, estimated bytes, remaining conjuncts, or coarse 
expression cost?
   4. What correctness and performance criteria should define acceptance? 
Should `OR` remain unchanged, and should selection-aware `FilterExec` 
integration be considered only if the `BinaryExpr` boundary is insufficient?
   
   After alignment, I will establish correctness, then compare baseline, B, C, 
and B+C with interleaved same-binary runs and profiles. Only then would I run 
31 `predicate_eval` cases with validation, TPC-H Q06 and the full suite with 
A/A drift controls, and repository-wide checks before proposing a production 
fix. I would genuinely appreciate your feedback on the semantic contract and 
evaluator boundary :)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to