Uh oh!
There was an error while loading. Please reload this page.
GH-39582: [C++][Acero] Increase size of Acero TempStack - #40007
Conversation
westonpace
commented
Feb 9, 2024
Hmm, this temp stack is only (I think) used in the hash-join. It's basically a stack allocator. Allocations made on the allocator should be RAII guarded to release when they finish. So either there is a bug and these are leaking somehow (I feel like this would be more reproducible) or maybe some of these stack variables are being help across a future boundary and that's causing some re-entrancy which causes the stack to run out of space (I like this explanation because it seems like it's environment specific and threading issues can often be environment statistic). Or, maybe it's only caused by certain input data? Either way, this stack doesn't take up very much memory in the grand scheme of things. I don't think there is much harm in increasing this value. |
westonpace
left a comment
There was a problem hiding this comment.
I'll approve this. @kou can feel free to merge if he wants to go ahead with this to unblock ruby.
If we can come up with a reproducible case I can investigate further but I probably won't have time to dedicate to trying to trigger this anytime soon.
kou
commented
Feb 10, 2024
Thanks for reviewing this! OK. Let's merge this for now. Let's investigating this later. (I hope that this can be reproducible on my environment...) @stenlarsson Could you update the PR description to describe this problem more deeper like the associated issue, this is not a real fix and we should investigate/fix this later before we merge this? We will use the PR title and description for commit message. |
pitrou
commented
Feb 12, 2024
Regardless of this, can we please check for stack overflows instead of letting them hang the process? |
kou
commented
Feb 13, 2024
Ah, it's a good idea. |
stenlarsson
commented
Feb 13, 2024
I have updated the description. It is unfortunately that you cannot reproduce the issue, because in a debug build the assertion fails reliably every time on my computer. |
pitrou
commented
Feb 13, 2024
Let's do it in this PR? |
zanmato1984
commented
Feb 14, 2024
Another issue of crash caused by this stack overflow has been reported in #39951. |
stenlarsson
commented
Feb 16, 2024
What exactly should happen in case of a stack overflow? |
pitrou
commented
Feb 16, 2024
A regular error if possible, or at least a controlled abort rather than memory corruption. |
stenlarsson
commented
Feb 22, 2024
Returning an error status is not an option since the |
pitrou
commented
Feb 22, 2024
You could either create a static constructor: template <typename T>
classTempVectorHolder {
friendclassTempVectorStack;
public:static Result<TempVectorHolder> Make(TempVectorStack* stack, uint32_t num_elements) {
TempVectorHolder holder{stack, nullptr, 0, num_elements};
ARROW_RETURN_NOT_OK(stack->alloc(num_elements * sizeof(T), &holder.data_, &holder.id_));
return holder;
}or, conversely, move the typed allocation API into TempVectorStack: classARROW_EXPORT TempVectorStack {
template <typename>
friendclassTempVectorHolder;
public:template <typename T>
Result<TempVectorHolder<T>> AllocateVector(uint32_t num_elements) {
TempVectorHolder holder{this, nullptr, 0, num_elements};
ARROW_RETURN_NOT_OK(alloc(num_elements * sizeof(T), &holder.data_, &holder.id_));
return holder;
} |
stenlarsson
commented
Feb 22, 2024
Can you really return a |
pitrou
commented
Feb 22, 2024
It's probably possible, yes. It's just a bunch of pointers and integers. |
stenlarsson
commented
Feb 22, 2024
If I understand this correctly, the purpose of the TempVectorHolder is to release the memory in the destructor, as if the vector was allocated on the stack. How can you return such an object? |
pitrou
commented
Feb 22, 2024
By defining a move constructor and assignment operator, like this: template <typename T>
classTempVectorHolder {
friendclassTempVectorStack;
public:~TempVectorHolder() {
if (stack_) {
stack_->release(id_, num_elements_ * sizeof(T));
}
}
TempVectorHolder& operator=(TempVectorHolder&& other) {
stack_ = other.stack_;
other.stack_ = NULLPTR;
data_ = other.data_;
other.data_ = NULLPTR;
id_ = other.id_;
num_elements_ = other.num_elements_;
return *this;
}
TempVectorHolder(TempVectorHolder&& other) {
*this = std::move(other);
}
T* mutable_data() { returnreinterpret_cast<T*>(data_); }
private:
TempVectorStack* stack_ = NULLPTR;
uint8_t* data_;
int id_;
uint32_t num_elements_;
}; |
stenlarsson
commented
Feb 23, 2024
I tried to implement this, but it is unfortunately beyond my abilities. If the TempVectorHolder returns a status, so does every method using it, and all methods using those methods, and so on. It is a huge change, and I got lost along the way. Raising an exception will have to do. |
Certain Acero execution plans can cause an overflow of the TempVectorStack initialized by the QueryContext, and increasing the size of the stack fixes the problem. I don't know exactly what causes the overflow, so I haven't written a test for it. Fixesapache#39582.
pitrou
commented
Feb 26, 2024
Ok, fair enough. I've now turned the exception into a regular check. |
pitrou
commented
Feb 26, 2024
@github-actions crossbow submit -g cpp |
Revision: ec3fd3b Submitted crossbow builds: ursacomputing/crossbow @ actions-fe3111b10f |
zanmato1984
commented
Feb 26, 2024
I'd post a non-binding +1. |
pitrou
commented
Feb 26, 2024
Thank you @zanmato1984 ! |
After merging your PR, Conbench analyzed the 7 benchmarking runs that have been run so far on merge-commit 9a7662b. There were no benchmark performance regressions. 🎉 The full Conbench report has more details. It also includes information about 2 possible false positives for unstable benchmarks that are known to sometimes produce them. |
We have had problems for a long time with a specific batch job that combines data from different sources. There is something in the data causing an Acero execution plan to hang or crash at random. The problem has been reproduced since Arrow 11.0.0, originally in Ruby, but it has also in Python. There is unfortunately no test case that reliably reproduces the issue in a release build. However, in a debug build we can see that the batch job causes an overflow on the temp stack in arrow/cpp/src/arrow/compute/util.cc:38. Increasing the size of the stack created in the Acero QueryContext works around the issue, but a real fix should be investigated separately. **This PR contains a "Critical Fix".** * Closes: #39582 Lead-authored-by: Sten Larsson <sten@burtcorp.com> Co-authored-by: Antoine Pitrou <antoine@python.org> Signed-off-by: Antoine Pitrou <antoine@python.org>
We have had problems for a long time with a specific batch job that combines data from different sources. There is something in the data causing an Acero execution plan to hang or crash at random. The problem has been reproduced since Arrow 11.0.0, originally in Ruby, but it has also in Python. There is unfortunately no test case that reliably reproduces the issue in a release build.
However, in a debug build we can see that the batch job causes an overflow on the temp stack in arrow/cpp/src/arrow/compute/util.cc:38. Increasing the size of the stack created in the Acero QueryContext works around the issue, but a real fix should be investigated separately.
This PR contains a "Critical Fix".