Skip to content

Conversation

@2010YOUY01
Copy link
Contributor

Which issue does this PR close?

Rationale for this change

See the issue for the background. If the optimizer made the wrong join order decision, and put a very small input at the probe side of NLJ, the NLJ operator now can handle it much faster than before.

For implementation, before it's always handling (one_left_row X right_batch) in the inner loop, this PR do join multiple left rows at once with the right batch, if the right batch is very small.

The NLJ microbench result, only Q13 is for this workload:

┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ Query        ┃     before ┃ improve-nlj-small-right ┃         Change ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ QQuery 1     │   85.31 ms │                85.75 ms │      no change │
│ QQuery 2     │  111.36 ms │               109.88 ms │      no change │
│ QQuery 3     │  180.99 ms │               181.56 ms │      no change │
│ QQuery 4     │  340.38 ms │               355.24 ms │      no change │
│ QQuery 5     │  248.62 ms │               231.90 ms │  +1.07x faster │
│ QQuery 6     │ 1680.89 ms │              1682.07 ms │      no change │
│ QQuery 7     │  233.65 ms │               234.83 ms │      no change │
│ QQuery 8     │ 1679.12 ms │              1675.63 ms │      no change │
│ QQuery 9     │  266.52 ms │               266.54 ms │      no change │
│ QQuery 10    │  544.66 ms │               544.71 ms │      no change │
│ QQuery 11    │  274.43 ms │               265.71 ms │      no change │
│ QQuery 12    │  275.11 ms │               274.72 ms │      no change │
│ QQuery 13    │   76.56 ms │                 1.88 ms │ +40.70x faster │
└──────────────┴────────────┴─────────────────────────┴────────────────┘

In DF49 it's around 4ms.

What changes are included in this PR?

  • Added one microbench query targeting small right input workload
  • Added one branch in the NLj's right input handling logic: if the current right batch is very small, try to join it with multiple left rows.

Are these changes tested?

This can be covered by existing tests: this additional path is not only triggered if the entire right input is small. For regular workloads, the final input batch can be also very small, so this new path can be triggered and tested.

Are there any user-facing changes?

@github-actions github-actions bot added the physical-plan Changes to the physical-plan crate label Sep 15, 2025
// Construct the Cartesian product between the specified range of left rows and the entire right_batch.
// Do not apply filters or update any bitmaps here.
let right_rows = right_batch.num_rows();
if l_row_count == 0 || right_rows == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can probably use this check even before calling process_left_range_join ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've checked and this case is not possible. I removed this condition and added an assertion outside.

return Ok(None);
}

let total_rows = l_row_count * right_rows;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like cartesian?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Added more comments to make it clear

@2010YOUY01
Copy link
Contributor Author

@comphead thanks for the review, comments are addressed.

I triggered extended tests locally, and it has passed: https://github.com/2010YOUY01/arrow-datafusion/actions/runs/17754340789

@Omega359
Copy link
Contributor

Is this ready for review again ? @comphead

@comphead
Copy link
Contributor

I think we slightly lost this PR, @2010YOUY01 are you okay to rebase the PR?

@2010YOUY01
Copy link
Contributor Author

close/re-open to rerun CI

@2010YOUY01 2010YOUY01 closed this Nov 11, 2025
@2010YOUY01 2010YOUY01 reopened this Nov 11, 2025
@2010YOUY01
Copy link
Contributor Author

I think we slightly lost this PR, @2010YOUY01 are you okay to rebase the PR?

Rebased 👌🏼

);

let l_row_cnt_ratio = self.batch_size / right_batch.num_rows();
if l_row_cnt_ratio > 10 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be follow up PR to make this fine tuning param be configurable by user?

Copy link
Contributor

@comphead comphead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @2010YOUY01 I think it is great.

@2010YOUY01 2010YOUY01 added this pull request to the merge queue Nov 13, 2025
Merged via the queue into apache:main with commit 2a6f3aa Nov 13, 2025
68 of 87 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

physical-plan Changes to the physical-plan crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Nested Loop Join more efficient for very small right input

3 participants