-
Notifications
You must be signed in to change notification settings - Fork 115
[onedpl][ranges] L3 Range based algo API and implementation (memory group) #2292
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
base: main
Are you sure you want to change the base?
Conversation
606f4fa
to
39d183b
Compare
22f1c16
to
0ec2d06
Compare
0ec2d06
to
470f339
Compare
int val1; //value initialization | ||
int val2; | ||
|
||
Elem_0(): val1() {} |
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 you explain in comments the idea of works with structure in which data is uninitialized and from this point may have any state?
std::ranges::borrowed_iterator_t<_R> | ||
operator()(_ExecutionPolicy&& __exec, _R&& __r) const | ||
{ | ||
const auto __dispatch_tag = oneapi::dpl::__ranges::__select_backend(__exec); |
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 another places I see the code like
const auto __dispatch_tag = oneapi::dpl::__ranges::__select_backend(__exec, __rng);
where we pass some __rng
into __select_backend
call.
From what it depends?
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.
good catch, thanks!
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 are more than one place like that, not all from 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.
Update: Actually, the original comment is not valid here.
"another places" - you mind the experimental code... In the product code we pass only policy into select_backend
function.
In "product" code we have random access ranges checks via concepts in the template description. So, we dont need do it the second time by passing ranges into the select_backend
function.
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 this mean we should move __select_backend
function from the call
const auto __dispatch_tag = oneapi::dpl::__ranges::__select_backend(__exec, __rng);
into experimental
namespace 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.
I don't see a reason for that...
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 I see on our code two funtions:
namespace oneapi
{
namespace dpl
{
namespace __ranges
{
template <typename _KernelName, typename... _Ranges>
oneapi::dpl::__internal::__hetero_tag<oneapi::dpl::__internal::__device_backend_tag>
__select_backend(const execution::device_policy<_KernelName>&, _Ranges&&...)
{
return {};
}
#if _ONEDPL_FPGA_DEVICE
template <unsigned int _Factor, typename _KernelName, typename... _Ranges>
oneapi::dpl::__internal::__hetero_tag<oneapi::dpl::__internal::__fpga_backend_tag>
__select_backend(const execution::fpga_policy<_Factor, _KernelName>&, _Ranges&&...)
{
return {};
}
#endif
} // namespace __ranges
} // namespace dpl
} // namespace oneapi
In the calls from experimental code you pass ranges into it, from non-experimental code you don't pass ranges.
But in any case ranges never used inside the __select_backend
function, as we may see.
Can you explain the overall idea of that?
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.
Related discussion: #1457 (comment). Mikhail's conclusion was to remove __select_backend
accepting ranges when we remove the experimental range algorithms.
However, moving it into the experimental namespace looks reasonable to disambiguate different __select_backend
but not critical in this PR.
Anyway, I suggest creating an issue to remove/refactor __select_backend
with ranges in order not to forget about it. or doing that in a separate PR.
|
||
oneapi::dpl::uninitialized_fill(std::forward<_ExecutionPolicy>(__exec), __first, __last, __value); | ||
|
||
return {__last}; |
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.
return {__last}; | |
return __last; |
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.
{}
is written intentionally, because it is equivalent to std::ranges::borrowed_iterator_t<_R>{__last}
(see the return type for the function).
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.
Will we have compile error in the next case?
return __last;
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.
Just for note: practically we never have returns with this form for cases when we return one value, as I seen.
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.
return __last;
tells the function returns an instance of "decltype(__last)"return {__last};
tells the function returns an instance of new type (return type), which is created from__last
.
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 two cents:
- I like (1) when a function is short: no duplication, you see the return type and {} suggests that is it not the type of
__last
. - I do not like (2). It is as (1) but it does not benefit from {}. Adding {} is not that extra number of symbol we should avoid.
- I do not like (3), because I think that having the return type in the function signature is more important.
- I like (4) when a function is large.
It's mostly a matter of preference (and now looks like bikeshedding), but __pattern_uninitialized_fill
is small, so I vote for (1).
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 it's a good topic to discuss shortly on our meeting.
UPD: the option (1) described at https://en.cppreference.com/w/cpp/language/list_initialization.html
And this variant has strongly defined semantic when make sense to apply in.
As far as we haven't compile errors here there is no reasons to apply it in the discussing cases I believe.
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 here fully with @dmitriy-sobolev . (1) when short, (4) when large.
I think these options make the code most clear and readable, which is my rule of thumb.
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.
After some discussion with @SergeyKopienko offline and exploring with godbolt, I realized that (1) is not explicitly invoking the constructor, but rather creating an initializer list, and then still using implicit construction of the return type from that initializer list.
https://godbolt.org/z/dTo84fooz
While not the exact same error message, (1) and (2) both don't work with explicit constructors. I guess (1) may give the perception that the type will be changing, (1) is not a shorthand equivalent to (4).
So, I guess perhaps I prefer (4) generally. For short functions, I think (1) and (2) are both fine, its probably not worth a whole lot more time discussing.
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.
...from cppreference.com :
https://en.cppreference.com/w/cpp/language/return.html
2) Uses [copy-list-initialization](https://en.cppreference.com/w/cpp/language/list_initialization.html) to construct the return value of the function.
https://en.cppreference.com/w/cpp/language/list_initialization.html
8) in a return statement with a brace-enclosed initializer list used as the return expression and list-initialization initializes the returned object
|
||
oneapi::dpl::destroy(std::forward<_ExecutionPolicy>(__exec), __first, __last); | ||
|
||
return {__last}; |
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.
return {__last}; | |
return __last; |
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.
See my explanation above.
ab2357f
to
ee23b56
Compare
…et_intersection high level API
…ric_difference, high level API
ee23b56
to
9aad8b3
Compare
…_tag_v<_Tag> || typename _Tag::__is_vector{}); and usage of some predefined functors" This reverts commit 0c2f14c.
This reverts commit 0740bda.
This reverts commit e7b7b63.
…l::__internal::__swap_fn" This reverts commit b725006.
This reverts commit 81fd65a.
This reverts commit 4f8f8f5.
This reverts commit dab8e9a.
This reverts commit 72b1f8e.
This reverts commit 6fdd2cb.
This reverts commit 31a659c.
This reverts commit 786044c.
This reverts commit dfee286.
This reverts commit 4aaa892.
… to another PR" This reverts commit 683585e.
…_swap" This reverts commit 8e8a1c5.
This reverts commit ce723c2.
…mpare" This reverts commit a3dc444.
This reverts commit d4048e2.
…t_symmetric_difference, high level API" This reverts commit 42a98e8.
…anges::set_intersection high level API" This reverts commit c3f437e.
…igh level API" This reverts commit ac103a1.
… high level API" This reverts commit d30bb56.
int val1; | ||
int val2; | ||
|
||
Elem() { val1 = 1; } |
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 we should have additional comments in the code why we don't initialize each field in constructors.
int val1; | ||
int val2; | ||
|
||
Elem_0(): val1() {} //val1 has a value initialization 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 think we should have additional comments in the code why we don't initialize each field in constructors.
template<typename _R> | ||
concept nothrow_random_access_range = | ||
std::ranges::range<_R> && nothrow_random_access_iterator<std::ranges::iterator_t<_R>> && | ||
nothrow_sentinel_for<std::ranges::sentinel_t<_R>, std::ranges::iterator_t<_R>>; |
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.
The corresponding concepts in the c++ standard are for exposition only. Should we move these concepts into an internal namespace then?
|
||
struct __uninitialized_move_fn | ||
{ | ||
template<typename _ExecutionPolicy, oneapi::dpl::ranges::nothrow_random_access_range _InRange, |
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.
Only the output range is required to be nothrow according to the proposal.
The same applies to uninitialized_copy
above.
{ | ||
template<typename _ExecutionPolicy, oneapi::dpl::ranges::nothrow_random_access_range _InRange, | ||
oneapi::dpl::ranges::nothrow_random_access_range _OutRange> | ||
requires std::constructible_from<std::ranges::range_value_t<_InRange>, std::ranges::range_reference_t<_OutRange>> |
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 seems that the order is confused.
requires std::constructible_from<std::ranges::range_value_t<_InRange>, std::ranges::range_reference_t<_OutRange>> | |
requires std::constructible_from<std::ranges::range_value_t<_OutRange>, std::ranges::range_reference_t<_InRange>> |
The same applies to uninitialized_move
below.
template<typename _S, typename _I> | ||
concept nothrow_sentinel_for = std::sentinel_for<_S, _I>; |
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 defacto imposes no restrictions on exceptions, it's just an alias. What is the purpose of that? Should it be implemented this way?
run_one_policy(alloc, oneapi::dpl::execution::seq, algo, checker, std::forward<decltype(args)>(args)...); | ||
run_one_policy(alloc, oneapi::dpl::execution::unseq, algo, checker, std::forward<decltype(args)>(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.
args
are forwarded multiple times.
|
||
#if _ENABLE_STD_RANGES_TESTING | ||
|
||
#include "std_ranges_memory_test.h" |
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 is no much sense to to have a separate header file - it is small, and only on cpp file uses it.
[onedpl][ranges] L3 Range based algo API and implementation (memory group)