-
Notifications
You must be signed in to change notification settings - Fork 3.7k
GH-46299: [C++][Compute] Don't use static inline const
for default options
#46303
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
Conversation
…fault options We use static variables for compute function option types. For example: https://github.com/apache/arrow/blob/068416bd411d6a8e2949f8ebcb2f80e2c302ef6b/cpp/src/arrow/compute/api_vector.cc#L127-L170 These option types are used for compute function options. For example: https://github.com/apache/arrow/blob/068416bd411d6a8e2949f8ebcb2f80e2c302ef6b/cpp/src/arrow/compute/api_vector.cc#L237 If we use `static inline const` for compute function options, these compute function options may be initialized BEFORE compute function option types. We use `static inline const` only for `rank`, `rank_quantile` and `rank_normal`: https://github.com/apache/arrow/blob/068416bd411d6a8e2949f8ebcb2f80e2c302ef6b/cpp/src/arrow/compute/kernels/vector_rank.cc#L382-L386 https://github.com/apache/arrow/blob/068416bd411d6a8e2949f8ebcb2f80e2c302ef6b/cpp/src/arrow/compute/kernels/vector_rank.cc#L399-L403 https://github.com/apache/arrow/blob/068416bd411d6a8e2949f8ebcb2f80e2c302ef6b/cpp/src/arrow/compute/kernels/vector_rank.cc#L416-L420 We can avoid this initialization order problem by deferring default compute function options initialization.
|
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.
This edit LGTM but I wonder is this initialize time long or it breaks some invariant?
Sorry, I couldn't understand your concerns... Could you explain them more? |
Aha. I wish they was an easy one-liner for the static function pattern. @bkietz |
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.
Sorry for this. I hadn't thought about initialization order.
Oh I've seen the issue here, so this is caused by initialization order in different translation units, and this patch changes this as call order. I've no problem now |
@@ -381,9 +381,13 @@ class RankMetaFunction : public RankMetaFunctionBase<RankMetaFunction> { | |||
} | |||
|
|||
RankMetaFunction() | |||
: RankMetaFunctionBase("rank", Arity::Unary(), rank_doc, &kDefaultOptions) {} | |||
: RankMetaFunctionBase("rank", Arity::Unary(), rank_doc, GetDefaultOptions()) {} |
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.
@pitrou well it's not one line, but...
: RankMetaFunctionBase("rank", Arity::Unary(), rank_doc, GetDefaultOptions()) {} | |
: RankMetaFunctionBase("rank", Arity::Unary(), rank_doc, [] { | |
static const auto kDefaultOptions = RankOptions::Defaults(); | |
return &kDefaultOptions; | |
}()) {} |
An IIFE can introduce a static lifetime object too
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.
IIFE?
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.
Sorry, Immediately Invoked Function Expression. I don't believe it's an official C++ concept
The expression is (<<nullary lambda for scope>>) (/* called with no args */)
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.
Do you think it would work to do something like:
// in .h file
template <typename OptionsType>
const FunctionOptions* StaticOptionsInit() {
static const auto kDefaultOptions = OptionsType::Defaults();
return &kDefaultOptions;
};
// in .cc file
class RankMetaFunction : public RankMetaFunctionBase<RankMetaFunction> {
public:
RankMetaFunction()
: RankMetaFunctionBase("rank", Arity::Unary(), rank_doc, StaticOptionsInit<RankOptions>()) {}
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 not sure. It seems to work on my machine but I really expected this to be an ODR violation. On my machine, the static local becomes a weak symbol so the linker ignores multiple definitions and just uses the first.
After reading through the provisions for multiple definitions, I tried using an inline variable and that seemed to work also:
template <typename OptionsType>
inline const auto kDefault = OptionsType::Defaults();
I'll keep thinking about this.
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.
Yes, sorry; that wasn't intended as a serious proposal for this issue- it was just another instance of me being surprised about ODR. I think your StaticOptionsInit
is fine; I can't convince myself looking at the standard but here's some precedent. Given that the issue was raised in LLVM and nobody exclaimed about ODR, my intuition is just incorrect here. (Those issues discuss the section group in which the static is emitted, which shouldn't bother us since we're not planning constexpr
construction of FunctionOptions)
Also StaticOptionsInit
is neat, and maybe we should extend it to be
template <typename T>
const T &Default() {
static const T kDefault;
return kDefault;
};
Then it might replace multiple instances of the same pattern in accessors which must return const std::shared_ptr<T>&
but don't have one, as in FileSource::path
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.
Also
StaticOptionsInit
is neat, and maybe we should extend it to be
Given the LLVM issue you linked to above, I'm now convinced we shouldn't do something like this :)
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.
Finally, what approach should we use here? :-)
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.
Sorry @kou. I think we can use either GetDefaultOptions
or the one suggested in #46303 (comment) . At your preference :)
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.
OK. Let's choose GetDefaultOptions()
here. GetDefaultOptions()
is more straightforward than #46303 (comment) because developers don't need to know IIFE and static variable lifetime.
After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit cf7d7e2. There were no benchmark performance regressions. 🎉 The full Conbench report has more details. It also includes information about 4 possible false positives for unstable benchmarks that are known to sometimes produce them. |
Rationale for this change
We use static variables for compute function option types. For example:
arrow/cpp/src/arrow/compute/api_vector.cc
Lines 127 to 170 in 068416b
These option types are used for compute function options. For example:
arrow/cpp/src/arrow/compute/api_vector.cc
Line 237 in 068416b
If we use
static inline const
for compute function options, these compute function options may be initialized BEFORE compute function option types.We use
static inline const
only forrank
,rank_quantile
andrank_normal
:arrow/cpp/src/arrow/compute/kernels/vector_rank.cc
Lines 382 to 386 in 068416b
arrow/cpp/src/arrow/compute/kernels/vector_rank.cc
Lines 399 to 403 in 068416b
arrow/cpp/src/arrow/compute/kernels/vector_rank.cc
Lines 416 to 420 in 068416b
We can avoid this initialization order problem by deferring default compute function options initialization.
What changes are included in this PR?
static function
instead ofstatic inline const
for default compute function optionsrank
,rank_quantile
andrank_normal
Are these changes tested?
Yes.
Are there any user-facing changes?
Yes.