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
Fix adjacent difference #6922
base: develop
Are you sure you want to change the base?
Fix adjacent difference #6922
Conversation
IteratorType2 s_last) { | ||
if constexpr (std::is_constructible_v<IteratorType2, IteratorType1>) { | ||
IteratorType1 s_first1(s_first), s_last1(s_last); | ||
KOKKOS_EXPECTS(first < s_first1 && s_last1 < 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.
don't you want to check instead:
( first > s_last1 ) || ( last < s_first1 )
?
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 is better. I fixed accordingly
IteratorType2 s_last) { | ||
if constexpr (std::is_constructible_v<IteratorType2, IteratorType1>) { | ||
IteratorType1 s_first1(s_first), s_last1(s_last); | ||
KOKKOS_EXPECTS(first > s_last1 || last < s_last1); |
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.
KOKKOS_EXPECTS(first > s_last1 || last < s_last1); | |
KOKKOS_EXPECTS(first > s_last1 || last < s_first1); |
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.
Are these checks correct for strided Views?
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.
If the iterators are convertible to each other, they should refer to the same stride, no?
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 was imagining a scenario where you could have iterators of the same type with the same stride but different starting pointers so that they don't overlap. For example, the location in memory would be alternating between the first iterator and the second iterator.
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.
True... How do incomparable iterators compare in C++?
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 you can check for all Kokkos::view::iterator
in case the referenced view is the same, even with non unit stride. This would case the most common case, 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.
We need to add a method to Kokkos::iterator
to compare internal views?
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, Kokkos::Experimental::Impl::RandomAccessIterator
. Note that you don't have direct access to its stride but you can get it from
auto next_iterator = iterator;
auto stride = &*(++next_iterator) - &*iterator;
(assuming the View access operator returns a reference). Otherwise, you would need to add a get_view
member function to RandomAccessIterator
to obtain the stride directly.
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 is nice. So the check would be something like this? (assuming the View access operator returns a reference)
auto next_first = first;
auto stride = &*(++next_first) - &*first;
if constexpr (std::is_constructible_v<IteratorType2, IteratorType1>) {
IteratorType1 s_first1(s_first), s_last1(s_last);
auto first_diff = &*first - &*s_first1;
bool is_no_overlap = first_diff % stride;
KOKKOS_EXPECTS(first > s_last1 || last < s_first1 || is_no_overlap);
}
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 does heavily rely on assumptions about the internal working of view iterators (for example the fact that you can not iterate on a 2D view like with a std::ranges::cartesian_product_view
) if this was ever to be extended to support it, the hypothesis here would start failing. I feel that making this check function friend of the iterator class would at least make this dependency explicit.
@@ -215,6 +215,28 @@ KOKKOS_INLINE_FUNCTION void expect_valid_range(IteratorType first, | |||
(void)last; | |||
} | |||
|
|||
// | |||
// Check if iterators are overlapped |
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.
// Check if iterators are overlapped | |
// Check if iterators are overlapping |
KOKKOS_EXPECTS(first > s_last1 || last < s_last1); | ||
} else if constexpr (std::is_constructible_v<IteratorType1, IteratorType2>) { | ||
IteratorType2 first2(first), last2(last); | ||
KOKKOS_EXPECTS(first2 > s_last || last2 < s_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.
KOKKOS_EXPECTS(first2 > s_last || last2 < s_last); | |
KOKKOS_EXPECTS(first2 > s_last || last2 < s_first); |
// avoid compiler complaining when KOKKOS_EXPECTS is no-op | ||
(void)first; | ||
(void)last; | ||
(void)s_first; | ||
(void)s_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.
// avoid compiler complaining when KOKKOS_EXPECTS is no-op | |
(void)first; | |
(void)last; | |
(void)s_first; | |
(void)s_last; |
KOKKOS_INLINE_FUNCTION void expect_no_overlap(IteratorType1 first, | ||
IteratorType1 last, | ||
IteratorType2 s_first, | ||
IteratorType2 s_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.
KOKKOS_INLINE_FUNCTION void expect_no_overlap(IteratorType1 first, | |
IteratorType1 last, | |
IteratorType2 s_first, | |
IteratorType2 s_last) { | |
KOKKOS_INLINE_FUNCTION void expect_no_overlap([[maybe_unused]] IteratorType1 first, | |
[[maybe_unused]] IteratorType1 last, | |
[[maybe_unused]] IteratorType2 s_first, | |
[[maybe_unused]] IteratorType2 s_last) { |
Hi, @masterleinad and @jbigot Thank you for your comments. I made a fix |
Not sure what the cases are, but I guess the thing this would now "wrongly" complain about is two layout stride views with offset 1 between them and stride 2? Do we care? |
otherwise looks good. |
That is true. We can create some valid (non-overlapping) cases with layout stride views, which do not satisfy this check. If there is a way to check that in a reasonable cost (say O (1)), it is certainly preferable to add that test. Maybe we can do some integer problem? |
So, it now suppresses "maybe overlapping" cases for safety. |
See #6922 (comment). I would just test specific types. |
You'll have to rebase on top of |
2db8971
to
a3accb1
Compare
@masterleinad Sorry about that. I thought I did that, but something went wrong. |
algorithms/src/std_algorithms/impl/Kokkos_AdjacentDifference.hpp
Outdated
Show resolved
Hide resolved
@@ -82,9 +82,17 @@ OutputIteratorType adjacent_difference_exespace_impl( | |||
return first_dest; | |||
} | |||
|
|||
// run | |||
// ranges shall not overlap |
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.
// ranges shall not overlap | |
// check for overlapping iterators |
::Kokkos::parallel_for( | ||
"Check", RangePolicy<ExecutionSpace>(ex, 0, 1), KOKKOS_LAMBDA(int) { | ||
Impl::expect_no_overlap(first_from, last_from, first_dest, last_dest); | ||
}); |
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.
::Kokkos::parallel_for( | |
"Check", RangePolicy<ExecutionSpace>(ex, 0, 1), KOKKOS_LAMBDA(int) { | |
Impl::expect_no_overlap(first_from, last_from, first_dest, last_dest); | |
}); | |
#ifdef KOKKOS_ENABLE_DEBUG | |
::Kokkos::parallel_for( | |
"Check", RangePolicy<ExecutionSpace>(ex, 0, 1), KOKKOS_LAMBDA(int) { | |
Impl::expect_no_overlap(first_from, last_from, first_dest, last_dest); | |
}); | |
#endif |
so we don't have to always pay for the launch overhead for another kernel.
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 is true. I also add this guard to the team level, since this check performs some computations
if constexpr (std::is_constructible_v<IteratorType2, IteratorType1>) { | ||
IteratorType2 first2(first), last2(last); | ||
IteratorType2 next_first2 = first2; | ||
ptrdiff_t stride = &*(++next_first2) - &*first2; |
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.
Instead, we could avoid executing this in the correct space if we allow to retrieve the View from the iterator.
This reverts commit 7d4833a.
Resolves #6890
This PR aims at adding assertion in
adjacent_difference
to disallow the overlapping of source and destination.expect_no_overlap
is newly added which is enabled in debug mode only. In the debug mode, it checksIt disallows the case 0 but still allows cases 1 and 2.
decltype(begin(const_a))
is constructible fromdecltype(begin(a))
.