Skip to content

[SPARK-XXXXX][SQL] Reuse AttributeSeq when normalizing a sequence in QueryPlan canonicalization - #58256

Open
yifei-yang-db wants to merge 1 commit into
apache:masterfrom
yifei-yang-db:reuse-attribute-seq-normalize-expressions
Open

[SPARK-XXXXX][SQL] Reuse AttributeSeq when normalizing a sequence in QueryPlan canonicalization#58256
yifei-yang-db wants to merge 1 commit into
apache:masterfrom
yifei-yang-db:reuse-attribute-seq-normalize-expressions

Conversation

@yifei-yang-db

Copy link
Copy Markdown

What changes were proposed in this pull request?

QueryPlan.normalizeExpressions(e, input: AttributeSeq) rewrites an expression's AttributeReference exprIds to positional ordinals using input's exprIdToOrdinal map, which is an instance-scoped lazy val. Several doCanonicalize implementations normalize a whole sequence with seq.map(QueryPlan.normalizeExpressions(_, attrs)), passing a bare Seq[Attribute] as input. The implicit Seq[Attribute] => AttributeSeq conversion is then re-applied on every element, so a fresh AttributeSeq (and a fresh exprIdToOrdinal map over all of attrs) is built for each element -- O(seq.size * attrs.size).

This PR adds an overload QueryPlan.normalizeExpressions(exprs: Seq[T], input: AttributeSeq): Seq[T] that binds the AttributeSeq a single time (so its lookup map is built once for the whole sequence, O(n)) and routes the affected canonicalization call sites through it: LogicalRelation, DataSourceV2ScanRelation, FileSourceScanExec, BatchScanExec, InMemoryRelation, InMemoryTableScanExec, SubqueryBroadcastExec, SubqueryAdaptiveBroadcastExec.

Why are the changes needed?

When a relation's own output is normalized against itself (output.map(normalizeExpressions(_, output))), the current code is quadratic in the number of output columns. Rebuilding the exprIdToOrdinal map once per column allocates a large amount of transient garbage on the driver while canonicalizing wide, unpruned relations; binding the AttributeSeq once makes it linear. The normalized result is unchanged.

Does this PR introduce any user-facing change?

No.

How was this patch tested?

Added a QueryPlanSuite test asserting the new Seq overload returns the same result as the per-element form. Existing canonicalization/sameResult coverage is unchanged.

Was this patch authored or co-authored using generative AI tooling?

No.

…QueryPlan canonicalization

QueryPlan.normalizeExpressions(e, input: AttributeSeq) rewrites an
expression's AttributeReference exprIds to positional ordinals using
input's exprIdToOrdinal map, an instance-scoped lazy val. Several
doCanonicalize implementations normalize a whole sequence with
seq.map(QueryPlan.normalizeExpressions(_, attrs)), passing a bare
Seq[Attribute]; the implicit Seq[Attribute] => AttributeSeq conversion is
then re-applied per element, rebuilding the lookup map over all of attrs
each time -- O(seq.size * attrs.size). Normalizing a relation's own output
against itself is therefore quadratic in the number of columns and
allocates a large amount of transient garbage on the driver for wide,
unpruned relations.

Add an overload normalizeExpressions(exprs: Seq[T], input: AttributeSeq)
that binds the AttributeSeq once (map built once, O(n)), and route the
canonicalization call sites through it: LogicalRelation,
DataSourceV2ScanRelation, FileSourceScanExec, BatchScanExec,
InMemoryRelation, InMemoryTableScanExec, SubqueryBroadcastExec,
SubqueryAdaptiveBroadcastExec. The normalized output is identical to the
per-element form.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant