-
Notifications
You must be signed in to change notification settings - Fork 798
[SYCL] Enforce constraints from sycl_ext_oneapi_reduction_properties
#16238
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
[SYCL] Enforce constraints from sycl_ext_oneapi_reduction_properties
#16238
Conversation
|
|
||
| template <typename BufferT, typename BinaryOperation, typename PropertyList> | ||
| template <typename BufferT, typename BinaryOperation, typename PropertyList, | ||
| typename = std::enable_if_t< | ||
| detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(BufferT vars, handler &cgh, BinaryOperation combiner, | ||
| PropertyList properties) { |
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 a little worried by this pattern, because it adds a template argument that is not present in the specification. I know this works, but I don't if it's legal from a SYCL specification perspective.
I'd like to hear @gmlueck's opinion 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.
In this case, it seems like you could avoid adding a new template parameter by instead adding enable_if to the handler parameter.
In general, implementing constraints seems a bit hacky in C++17 because you need to use SFINAE, and this sometimes requires adding a new template parameter or even changing a non-templated member function into a templated one. I don't know if we have a strict set of rules about whether this is completely legal. We know that changing a conversion operator from non-templated to templated changes the semantics, so we don't do this. (@aelovikov-intel has used a "mix-in" technique to avoid this for the vec class.) For the other cases, I'd advise that we avoid adding new template parameters if there is an alternative.
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.
by instead adding enable_if to the handler parameter.
I don't remember seeing such an approach anywhere in the codebase, do you have some pointers?
For the other cases, I'd advise that we avoid adding new template parameters if there is an alternative.
I thought we're adding that extra parameter almost in every corner of the project right now...
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.
Can't you do something like this:
template <typename BufferT, typename BinaryOperation, typename PropertyList>
auto reduction(BufferT vars, std::enable_if<detail::is_valid_reduction_prop_list<PropertyList>, handler>::type &cgh, BinaryOperation combiner,
PropertyList properties)
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.
That isn't the question. What I'm saying is that I'm not aware of any place in the codebase where we use such pattern instead of extra template parameter. Why should we do anything differently 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.
I guess it's just general concern that adding another template parameter might cause some code to break.
@Pennycook, @rolandschulz: Didn't we see some previous example where adding a new template parameter to accessor caused something to break, even though the template parameter had a default value? Maybe I'm misremembering?
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.
That isn't the question. What I'm saying is that I'm not aware of any place in the codebase where we use such pattern instead of extra template parameter. Why should we do anything differently here?
FWIW, there are quite a few places (e.g., in the group algorithms) where we use enable_if on the return type instead of using an extra template parameter:
llvm/sycl/include/sycl/ext/oneapi/experimental/user_defined_reductions.hpp
Lines 46 to 49 in e9cbb87
| // ---- reduce_over_group | |
| template <typename GroupHelper, typename T, typename BinaryOperation> | |
| std::enable_if_t<(is_group_helper_v<GroupHelper>), T> | |
| reduce_over_group(GroupHelper group_helper, T x, BinaryOperation binary_op) { |
Didn't we see some previous example where adding a new template parameter to accessor caused something to break, even though the template parameter had a default value? Maybe I'm misremembering?
I think you're referring to this issue: #10057. Even if the same problem can't actually be triggered for this overload, the fact we hit this unexpected issue with accessor does make me think that it would be safer to avoid adding extra templates just in case.
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 with @aelovikov-intel, this is not an uncommon pattern for SFINAE on public APIs. If we believe this to be problematic, we may have to address this in other places as well, but the solution of SFINAE'ing an argument assumes that the methods have arguments, which holds in this case, but may not hold for all.
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 might be wrong about this, but if we wanted to be consistent, couldn't we always implement SFINAE using the return type? Every function has a return type (but sometimes that type is void).
I appreciate it's a bit ugly to employ in a case like this where the implementation was previously relying on a deduced return type, but I think it's doable. Is there a reason that we couldn't implement this as something like:
template <typename BufferT, typename BinaryOperation, typename PropertyList>
std::enable_if_t<detail::is_valid_reduction_prop_list<PropertyList>,
detail::reduction_t<BufferT, BinaryOperation, PropertyList>>
reduction(BufferT vars, handler &cgh, BinaryOperation combiner, PropertyList properties) { ... }As an added bonus, all the SFINAE would be in a place where we could legally replace it with a requires statement when we switch to C++20:
template <typename BufferT, typename BinaryOperation, typename PropertyList>
requires ValidReductionPropertyList<PropertyList>
auto reduction(BufferT vars, handler &cgh, BinaryOperation combiner, PropertyList properties) { ... }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 personally prefer SFINAE on return type, with the exceptions like this one when it's deduced :) ...and ctors.
| typename = std::enable_if_t< | ||
| detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(BufferT vars, handler &cgh, BinaryOperation combiner, | ||
| PropertyList properties) { |
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 tempted to say I would prefer a static_assert on whether the properties are valid. That way we can control the message telling the user why they're not allowed to make the call they're trying to do.
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 don't that's valid in this case. The specification says that this is a Constraint, and ISO C++ says (emphasis mine):
[Note 1: Failure to meet such a condition results in the function's silent non-viability. — end note]
We could do this if we changed from a Constraint to a Mandate, but I don't know enough about why to favor one over the other.
b908477 to
89000af
Compare
89000af to
a71b1ba
Compare
Pennycook
left a comment
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.
Beautiful! Thank you for making this change, and sorry for being a pain.
|
@intel/llvm-reviewers-runtime , @steffenlarsen is on vacation, can someone else review this? |
| std::ignore = | ||
| sycl::reduction(r, sycl::plus<int>{}, | ||
| sycl_exp::properties(sycl_exp::initialize_to_identity)); |
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.
Should we augment the test for deterministic property as well?
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.
Why? The change here is about "negative" behavior, this particular line acts less like a test/check and more like "context"/"back-reference". Positive behavior tests are expected to have been added in the original PR introducing these properties.
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 see. Looks good then. Also, please update the PR description to summarize the changes in this PR.
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 title already says it all. What else do you want to see there?
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.
It is not clear from the title what "Constraints"/"Negative behavior" you are referring to.
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 we go. All constraints is the keyword I'm looking for.
That's probably language barrier :) In the absence of "some" I'd just assume "all" by default :)
What's the downside of quoting the constraints...
You haven't answered my question:
What would you do if different methods had slightly different constraints? Would you list all of them separately?
I can't imagine the answer for that would be copy-pasting them all into this PR. And if we agree that we wouldn't do that for such more complex case, I can't see how a simpler one deserves a longer PR description.
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.
What would you do if different methods had slightly different constraints? Would you list all of them separately?
Well, if the constraints were to be slightly different, we could still try to summarize them, without being overly specific.
To quote LLVM's developer policy on commit messages (https://llvm.org/docs/DeveloperPolicy.html#commit-messages):
Most importantly, the contents of the message should be carefully written to convey the rationale of the change (without delving too much in detail). It also should avoid being vague or overly specific.
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 still try to summarize them
That would be "implement constraints from ", wouldn't it?
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.
That would be "implement constraints from ", wouldn't it?
Depends on how different the constraints are. If they are just "slightly" different, I would have come up with a better commit message than "implement constraints". If they are very different, to the extent of being unrelated, I would have tried to break PRs into smaller ones.
For the record, this is not a blocker from me since changes in SYCL RT looks good. So, if you feel strongly about not adding a commit message, please feel free to proceed with the merge.
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.
So, if you feel strongly about not adding a commit message, please feel free to proceed with the merge.
I feel strongly about [NOT] quoting the spec :)
uditagarwal97
left a comment
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.
LGTM. Just one minor comment.
No description provided.