Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recursive spill for RowNumber #8654

Closed
wants to merge 1 commit into from

Conversation

duanmeng
Copy link
Collaborator

@duanmeng duanmeng commented Feb 2, 2024

At present, RowNumber only supports single-level spill partitions.
Once a spill takes place, it won't be spilled again. This means that
even if a restored spill partition uses an excessive amount of memory,
it still cannot be reclaimed. Therefore, it would be beneficial to implement
recursive spill support, as outlined in
https://facebookincubator.github.io/velox/develop/spilling.html.

1 Advance 'spillPartitionBits_' based on the partition bit of the currently
restoring spill partition to prepare for potential subsequent spills (recursive).

2 During the recursive spill input process, read the spilled input data from the
previous spill run via 'spillInputReader_', then spill it back into several sub-partitions.
Then restore one of the newly spilled partitions and reset 'spillInputReader' accordingly.

3 It's crucial to separate the recursive input spill process from the spill process itself,
as it can be time-consuming and should yield if it exceeds the CPU time limit.

In summary, the first spill operation writes all data to the disk. Subsequently,
RowNumber reads the data from the disk one partition at a time and clears
the data from the restored partition once it has been processed (#8413).
In the case of a recursive spill, only the data from the currently restored partition
in memory is spilled to the disk (next level), and only one partition from the next
level is loaded back into memory.

The data layout may look like as follows.

Memory Disk
No-Spill [P0, P1] []
First Spill [] [P0, P1]
Restore [P0] [P1]
Second Spill [] [P0-0, P0-1, P1]
Restore [P0-0] [P0-1, P1]
Processed [] [P0-1, P1]
Restored [P0-1] [P1]
Processed [] [P1]
Restored [P1] []

Copy link

netlify bot commented Feb 2, 2024

Deploy Preview for meta-velox canceled.

Name Link
🔨 Latest commit fb776a0
🔍 Latest deploy log https://app.netlify.com/sites/meta-velox/deploys/665ee49477bce30008aac63e

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Feb 2, 2024
@duanmeng duanmeng requested review from xiaoxmeng and mbasmanova and removed request for xiaoxmeng February 2, 2024 09:07
@duanmeng duanmeng force-pushed the recursiveSpill branch 2 times, most recently from 490c6c6 to e41d72b Compare February 3, 2024 08:07
Copy link
Contributor

@xiaoxmeng xiaoxmeng left a comment

Choose a reason for hiding this comment

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

@duanmeng thanks for the change and we might need to move the recursive input spill out of spill() call.

velox/exec/RowNumber.h Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Outdated Show resolved Hide resolved
velox/exec/Spill.h Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/tests/RowNumberTest.cpp Outdated Show resolved Hide resolved
velox/exec/tests/RowNumberTest.cpp Outdated Show resolved Hide resolved
velox/exec/tests/RowNumberTest.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.h Outdated Show resolved Hide resolved
@duanmeng duanmeng force-pushed the recursiveSpill branch 2 times, most recently from c0d7b31 to 2bcd1bd Compare March 3, 2024 05:56
Copy link
Contributor

@xiaoxmeng xiaoxmeng left a comment

Choose a reason for hiding this comment

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

@duanmeng LGTM. Thanks!

velox/exec/tests/RowNumberTest.cpp Outdated Show resolved Hide resolved
velox/exec/tests/RowNumberTest.cpp Show resolved Hide resolved
velox/exec/tests/RowNumberTest.cpp Show resolved Hide resolved
newQueryCtx(memoryManager, executor_, kMemoryCapacity * 2);

struct {
int32_t numSpill;
Copy link
Contributor

Choose a reason for hiding this comment

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

s/numSpill/numSpills/

velox/exec/tests/RowNumberTest.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
@facebook-github-bot
Copy link
Contributor

@xiaoxmeng has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
@duanmeng
Copy link
Collaborator Author

@xiaoxmeng Hi Xuan, I've made a small refactor based on your newly simplified RowNumber spill implementation. Could you please help to take another look? Thanks.

@duanmeng duanmeng force-pushed the recursiveSpill branch 2 times, most recently from 8b94e78 to 49bb0d1 Compare March 14, 2024 03:13
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
velox/exec/RowNumber.cpp Show resolved Hide resolved
velox/exec/RowNumber.cpp Outdated Show resolved Hide resolved
@facebook-github-bot
Copy link
Contributor

@xiaoxmeng has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

Copy link
Contributor

@bikramSingh91 bikramSingh91 left a comment

Choose a reason for hiding this comment

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

Looks good, just a few nits. Thank you for adding recursive spilling to this Operators.

Can you please also update Operators.rst(L919) to mention that RowNumber Operator now supports spilling.


void RowNumber::addInputInternal(const RowVectorPtr& input, bool fromSpill) {
ensureInputFits(input);
if (!fromSpill && inputSpiller_ != nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

do we expect to encounter this condition in any case? if not, should this be a VELOX_CHECK instead of a no-op return ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this happens after the first spill before the noMoreInput stage. This function is unreadable and hard to understand. I will refactor it and use addSpillInput. Thanks for your advice.

return;
}

if (input == nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto as comment on L485

}

if (yield_) {
VELOX_CHECK_NULL(input_);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: do we need this check? since we only reach here if condition on L227 is true where input_ == nullptr

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed, thanks.

Copy link
Collaborator Author

@duanmeng duanmeng left a comment

Choose a reason for hiding this comment

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

@bikramSingh91 Thanks for your review, I've resolved all the comments and updated the doc. Could you help take another look? Thanks a lot.

}

if (yield_) {
VELOX_CHECK_NULL(input_);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed, thanks.


void RowNumber::addInputInternal(const RowVectorPtr& input, bool fromSpill) {
ensureInputFits(input);
if (!fromSpill && inputSpiller_ != nullptr) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this happens after the first spill before the noMoreInput stage. This function is unreadable and hard to understand. I will refactor it and use addSpillInput. Thanks for your advice.

@duanmeng duanmeng force-pushed the recursiveSpill branch 2 times, most recently from 3330f39 to 8178c03 Compare March 30, 2024 12:30
@@ -97,8 +101,14 @@ void RowNumber::addInput(RowVectorPtr input) {
}

void RowNumber::addSpillInput() {
ensureInputFits(input_);
Copy link
Contributor

Choose a reason for hiding this comment

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

this will be called twice when invoked via addInput(), will this result in the reservation being increased twice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, the addInput is called before noMoreInput, while addInputSpill is called after noMoreInput.
The addInputSpill is specifically called right after the spillInputReader_ reads a batch in either the restoreNextSpillPartition or getOutput. The spillInputReader_ is null before noMoreInput is called, and it first becomes non-null at the end of restoreNextSpillPartition when the noMoreInput is called.

@duanmeng duanmeng force-pushed the recursiveSpill branch 2 times, most recently from 9bf9bd8 to b814c65 Compare April 10, 2024 08:30
@duanmeng
Copy link
Collaborator Author

@bikramSingh91 Hi Bikram, any further comments? Thanks.

@duanmeng
Copy link
Collaborator Author

@mbasmanova Hi Masha, all the prerequisite PRs have been merged, could you please help take another look? Thanks.

@mbasmanova
Copy link
Contributor

@duanmeng Thank you for adding "The data layout may look like as follows." section to PR description. It is very helpful.

Spilling is rather complicated and we are seeing many bugs in it. I wonder how much testing have been done for this change and what's the confidence of it being bug free. I don't believe we have fuzzer coverage for RowNumber operator. Would that be necessary to ensure there are no (or very few) bugs?

Also, I wonder what's the motivation for this change. Is there a real-world scenario that benefits from this change and justifies added complexity and maintenance costs. Would you describe that scenario? In this scenario, what's the impact on latency and CPU usage of spilling? How much CPU and wall time a query would use without spilling and how does that compare to spilling?

@duanmeng duanmeng force-pushed the recursiveSpill branch 2 times, most recently from 220b463 to 4e09fff Compare April 10, 2024 16:28
@facebook-github-bot
Copy link
Contributor

@xiaoxmeng has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

tanjialiang

This comment was marked as resolved.

@facebook-github-bot
Copy link
Contributor

@xiaoxmeng merged this pull request in 4963d71.

Copy link

Conbench analyzed the 1 benchmark run on commit 4963d711.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants