Skip to content

[SPARK-59706][SQL] Hoist loop-invariant left row out of the AS-OF join inner scan - #58959

Closed
david-mollitor-db wants to merge 1 commit into
apache:branch-4.xfrom
david-mollitor-db:asof-hoist-withleft-4.x
Closed

david-mollitor-db wants to merge 1 commit into
apache:branch-4.xfrom
david-mollitor-db:asof-hoist-withleft-4.x

Conversation

@david-mollitor-db

Copy link
Copy Markdown
Contributor

This backports #58879 (SPARK-59706) to branch-4.x. It is a clean cherry-pick of the master commit; the change is identical.

What changes were proposed in this pull request?

In SortMergeAsOfJoinScanner
(sql/core/src/main/scala/org/apache/spark/sql/execution/joins/SortMergeAsOfJoinExec.scala), the
per-left-row scan over the buffered right group (findBestBackwardForward and
findBestForwardNearest) rebinds both sides of the shared JoinedRow on every iteration via
joinedRow.withLeft(leftRow).withRight(rightRow). The left row is fixed for the duration of the
scan, so this binds joinedRow.withLeft(leftRow) once before the loop and keeps only
joinedRow.withRight(rightRow) inside it.

Why are the changes needed?

The inner scan runs once per buffered right row for every left row, so withLeft was called
redundantly on each iteration with an argument that never changes within a scan. JFR profiling of
AsOfJoinBenchmark showed JoinedRow.withLeft/withRight among the hotter frames in the
(interpreted) scanner. Binding the left side once removes the redundant per-row call. This is
behavior-preserving: JoinedRow.withLeft only stores the left-row reference, nothing mutates the
left side within a scan, and findNext re-binds both sides before projecting the output row.

Does this PR introduce any user-facing change?

No.

How was this patch tested?

Backport of #58879 (SPARK-59706); the change is identical to the master PR. Existing AS-OF join
tests cover it (SortMergeAsOfJoinSuite, DataFrameAsOfJoinSuite, AsOfJoinSQLSuite, and
AsOfJoinSortMergeSQLSuite), and branch-4.x CI runs them.

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

Generated-by: Isaac

This pull request and its description were written by Isaac.

…n inner scan

### What changes were proposed in this pull request?

In `SortMergeAsOfJoinScanner`
(`sql/core/src/main/scala/org/apache/spark/sql/execution/joins/SortMergeAsOfJoinExec.scala`), the
per-left-row scan over the buffered right group (`findBestBackwardForward` and
`findBestForwardNearest`) rebinds both sides of the shared `JoinedRow` on every iteration via
`joinedRow.withLeft(leftRow).withRight(rightRow)`. The left row is fixed for the duration of the
scan, so this binds `joinedRow.withLeft(leftRow)` once before the loop and keeps only
`joinedRow.withRight(rightRow)` inside it.

### Why are the changes needed?

The inner scan runs once per buffered right row for every left row, so `withLeft` was called
redundantly on each iteration with an argument that never changes within a scan. JFR profiling of
`AsOfJoinBenchmark` showed `JoinedRow.withLeft`/`withRight` among the hotter frames in the
(interpreted) scanner. Binding the left side once removes the redundant per-row call. This is
behavior-preserving: `JoinedRow.withLeft` only stores the left-row reference, nothing mutates the
left side within a scan, and `findNext` re-binds both sides before projecting the output row.

### Does this PR introduce _any_ user-facing change?

No.

### How was this patch tested?

Existing tests pass: `SortMergeAsOfJoinSuite`, `DataFrameAsOfJoinSuite`, `AsOfJoinSQLSuite`, and
`AsOfJoinSortMergeSQLSuite` (89 tests).

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

Generated-by: Isaac

Co-authored-by: Isaac <no-reply@databricks.com>
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