-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
GH-44491: [C++] StatusConstant- cheaply copied const Status #44493
base: main
Are you sure you want to change the base?
Conversation
|
cpp/src/arrow/status.h
Outdated
if (ARROW_PREDICT_TRUE(state_ == NULL)) return; | ||
if (ARROW_PREDICT_FALSE(state_->is_static)) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to have a single condition here to make it obvious that the rest of the function is the cold code:
if (ARROW_PREDICT_TRUE(state_ == NULL || state_->is_static)) return;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this is more obvious for human readers, but I think the priority here is to make it obvious to the compiler that the hottest condition is state_ == NULL
, independent of any other consideration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm saying it would be better to the compiler as well not only humans. What the previous code achieved with the ARROW_PREDICT_FALSE
was telling the compiler that DeleteState()
is code and shouldn't be inlined. Since lots of Status
are destroyed all over the place, that reduces binary size and avoids pollution of the I-cache with cold code.
if (ARROW_PREDICT_FALSE(state_ != NULL)) {
DeleteState();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I see what you mean, how about:
if (ARROW_PREDICT_TRUE(state_ == NULL)) return; | |
if (ARROW_PREDICT_FALSE(state_->is_static)) return; | |
if (ARROW_PREDICT_FALSE(state_ != NULL)) { | |
if (ARROW_PREDICT_FALSE(state_->is_static)) return; | |
delete state_; |
Then it should still be clear that State::~State()
is cold. I could also explicitly move the definition of State::~State()
into status.cc
, which should prevent inlining
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the previous structure is the best: call DeleteState
after a single unlikely condition. And the is_static
check lives inside DeleteState
.
0634658
to
0a5005e
Compare
ARROW_CHECK_NE(code, StatusCode::OK) << "Cannot construct ok status constant"; | ||
} | ||
|
||
operator Status() const { // NOLINT(runtime/explicit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could alternatively make StatusConstant a subclass of Status
@pitrou @felipecrv which benchmark would be convincing as a demonstration this doesn't slow us down? I don't think we have an explicit status or result benchmark For now (since it seems to me to have the deepest Status bubbling stack), @ursabot please benchmark command=cpp-micro --suite-filter=grouper |
We do, but for some reason I had stuffed it in arrow/cpp/src/arrow/type_benchmark.cc Lines 545 to 555 in 1b40800
|
@ursabot please benchmark command=cpp-micro --suite-filter=type |
Benchmark runs are scheduled for commit f7d3fff. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete. |
Thanks for your patience. Conbench analyzed the 3 benchmarking runs that have been run so far on PR commit f7d3fff. There were 4 benchmark results indicating a performance regression:
The full Conbench report has more details. |
Can you check the binary size impact with The performance impact is very tiny but everywhere a |
} | ||
|
||
// We would prefer that this destructor *not* be inlined, since the vast majority of | ||
// Statuses are OK and so inlining would superflously increase binary size. | ||
Status::State::~State() = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My guess is that it's better to have this inlined, but avoid inlining DeleteState
(which is effectively what the previous implementation was achieving with
if (ARROW_PREDICT_FALSE(state_ != NULL)) {
DeleteState();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try writing out the conditions and keeping DeleteState non-inline
StatusConstant(StatusCode code, std::string msg, | ||
std::shared_ptr<StatusDetail> detail = nullptr) | ||
: state_{code, std::move(msg), std::move(detail), /*is_constant=*/true} { | ||
ARROW_CHECK_NE(code, StatusCode::OK) << "Cannot construct ok status constant"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we could, but it's counter to the reason of StatusConstant
existing. Message should be StatusConstant is not meant to be used to construct an OK status"
Rationale for this change
It'd be convenient to construct placeholder error Status cheaply.
What changes are included in this PR?
A constant error Status can now be constructed wrapped in a function like
Copying the constant status is relatively cheap (no heap interaction), so it's suitable for use as a placeholder error status.
Added
bool Status::State::is_constant
which causes copies to be shallow and skips destruction. AlsoStatus::state_
is a const pointer now; this helps to ensure that it is obvious when we mutate state_ (as in AddContextLine).Are these changes tested?
Minimal unit test added. The main consideration is probably benchmarks to make sure hot paths don't get much slower.
Are there any user-facing changes?
This API is not currently public; no user-facing changes.