xumingming opened a new pull request, #57703:
URL: https://github.com/apache/spark/pull/57703

   ### What changes were proposed in this pull request?
   
   Wire `ExpandExec` into the whole-stage codegen subexpression elimination 
framework:
   
   - All branch expressions are bound once up front and analyzed together. A 
subexpression shared across branches (or repeated within a branch) is evaluated 
once per input row, before the branch loop, and each branch references the 
cached value. This is semantics-preserving because every branch of an Expand 
consumes the same input row.
   - Both the branch-invariant column generation and the per-branch switch/case 
generation resolve repeated subtrees to the cached values.
   - The switch/case function splitting (SPARK-35329) now passes the eliminated 
subexpression variables into the split functions as parameters, so large 
Expands keep compiling correctly.
   - Gated by the existing `spark.sql.subexpressionElimination.enabled` conf 
(no new configuration).
   - Adds an `ExpandBenchmark` case.
   
   ### Why are the changes needed?
   
   Conditional-aggregate rollup queries (e.g. "N-day active users" dashboards) 
stack many conditional aggregates whose conditions share one expensive 
subexpression:
   
   ```sql
   SELECT
     COUNT(DISTINCT IF(datediff(date '2026-01-01',
       from_unixtime(unix_timestamp(ts, 'yyyy-MM-dd HH:mm:ss'))) <= 1, uid, 
NULL)) AS uv_1d,
     COUNT(DISTINCT IF(datediff(date '2026-01-01',
       from_unixtime(unix_timestamp(ts, 'yyyy-MM-dd HH:mm:ss'))) <= 7, uid, 
NULL)) AS uv_7d,
     SUM(IF(datediff(date '2026-01-01',
       from_unixtime(unix_timestamp(ts, 'yyyy-MM-dd HH:mm:ss'))) <= 1, 1, 0)) 
AS pv_1d
     -- ... more conditional aggregates over longer windows
   FROM traffic
   ```
   
   `RewriteDistinctAggregates` gives each distinct group its own Expand branch 
with the condition expression verbatim, so the shared subexpression is compiled 
into every branch body and re-evaluated once per branch per input row. In the 
benchmark query above it is evaluated 18 times per input row (9 
distinct-aggregate conditions + 9 regular-aggregate conditions across 10 
branches); with this PR it is evaluated once.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. Query results are unchanged; only the number of evaluations per input 
row changes.
   
   ### How was this patch tested?
   
   - Extended the `WholeStageCodegenSuite` test "Expand should eliminate common 
subexpressions across branches" (SQL-based, via conditional `COUNT DISTINCT` 
aggregates):
     - asserts the shared subexpression is evaluated once per input row with 
elimination enabled, and once per branch with it disabled;
     - forces both switch/case code paths deterministically via 
`spark.sql.codegen.methodSplitThreshold` (a tiny threshold forces the 
SPARK-35329 function splitting, a huge threshold keeps the bodies inline), 
asserting on the generated `switchCaseCode` functions and verifying correctness 
on both paths.
   - Ran the full `WholeStageCodegenSuite` (57 tests passed).
   - Benchmark: new `ExpandBenchmark` case modeling the rollup above, 5M rows, 
3 iterations, Apple M4 Pro / JDK 17 (local run): shared subexpression 
evaluations per input row 18 -> 1; average runtime 82485 ms -> 51457 ms (15701 
-> 9756 ns/row), 1.6X faster.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   No.
   


-- 
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