Yang Jie created SPARK-59603:
--------------------------------

             Summary: Memoize a subexpression repeated inside a conditional 
branch
                 Key: SPARK-59603
                 URL: https://issues.apache.org/jira/browse/SPARK-59603
             Project: Spark
          Issue Type: Improvement
          Components: SQL
    Affects Versions: 5.0.0
            Reporter: Yang Jie


Subexpression elimination cannot reach a subexpression that is repeated inside 
a single branch of an {{if}} or {{case when}}, and the reason is structural 
rather than an oversight: it evaluates its candidates before the projection, so 
{{EquivalentExpressions}} only collects what is always evaluated 
({{ConditionalExpression.alwaysEvaluatedInputs}}) plus what every branch of a 
group shares ({{branchGroups}}, an intersection). A subexpression repeated 
inside one branch body and nowhere else is in no group, so it is evaluated once 
per occurrence for every row that reaches the branch.

Since SPARK-58818 a {{With}} is evaluable and memoizes per evaluation, which 
covers exactly this: the definition sits inside the branch, so nothing is 
computed for a row that does not reach it, and the references read one value.

This adds an optimizer rule, {{MemoizeCommonExpressionsInBranches}}, that 
rewrites the tallest repeated subexpression of each conditionally evaluated 
child of an {{if}} / {{case when}} into a {{With}}. It runs late (after the 
simplification rules, so what it memoizes is what survives them) and leaves the 
{{With}} in the branch, which is the shape {{RewriteWithExpression}} keeps 
anyway.

Measured on {{select case when id < 5 then udf(id) + udf(id) else 0 end from 
range(0, 10, 1, 1)}} with a counting UDF: 5 evaluations with the rule, 10 
without.

Guarded by {{spark.sql.optimizer.memoizeCommonExpressionsInBranches.enabled}}, 
default false, so plans are unchanged unless it is turned on. A candidate is 
skipped when it is cheap ({{CollapseProject.isCheap}}), when it holds a 
common-expression reference or a lambda variable (neither can be evaluated 
where the definition would sit), when it holds an aggregate, window or 
generator expression (the planner looks for those where they stand), or when it 
holds a subquery expression. A branch that already holds a {{With}} is left 
alone, since {{RewriteWithExpression}} defers a nested {{With}} to a pass that 
no longer runs by then.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

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

Reply via email to