Skip to content

[oneDPL] Add more parallel range algorithms #614

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

Merged
merged 5 commits into from
May 2, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 186 additions & 4 deletions source/elements/oneDPL/source/parallel_api/parallel_range_api.rst
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ predefined function objects which static function call operators have the requir
The following differences to the standard C++ range algorithms apply:

- Parallel range algorithms cannot be used in constant expressions.
- The execution policy parameter is added.
- The oneDPL execution policy parameter is added.
- Output data sequences are defined as ranges, not iterators.
- Both input and output ranges must support random access.
- As a rule, both input and output ranges must be sized.
@@ -30,7 +30,7 @@ The following differences to the standard C++ range algorithms apply:
[*Note*: An example of an infinite range is ``std::views::repeat`` with no bound. -- *end note*]

- For algorithms with bounded output ranges, processing may not need to go over all the input data.
In that case, the returned value usually contains iterators pointing to the positions past the last elements
In that case, the returned value contains iterators pointing to the positions past the last elements
processed according to the algorithm semantics.
- ``for_each`` does not return its function object.

@@ -153,6 +153,18 @@ Element Search Operations
std::ranges::borrowed_iterator_t<R>
find_if_not (ExecutionPolicy&& pol, R&& r, Pred pred, Proj proj = {});

// find_first_of
template<typename ExecutionPolicy, std::ranges::random_access_range R1,
std::ranges::random_access_range R2, typename Pred = std::ranges::equal_to,
typename Proj1 = std::identity, typename Proj2 = std::identity>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R1> && std::ranges::sized_range<R2> &&
std::indirectly_comparable< std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>,
Pred, Proj1, Proj2 >
std::ranges::borrowed_iterator_t<R1>
find_first_of (ExecutionPolicy&& pol, R1&& r1, R2&& r2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {});

// adjacent_find
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
@@ -164,6 +176,54 @@ Element Search Operations
std::ranges::borrowed_iterator_t<R>
adjacent_find (ExecutionPolicy&& pol, R&& r, Pred pred = {}, Proj proj = {});

}

Minimum and Maximum
+++++++++++++++++++

.. code:: cpp

// Defined in <oneapi/dpl/algorithm>

namespace oneapi::dpl::ranges {

// min
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
std::indirect_strict_weak_order< std::projected<std::ranges::iterator_t<R>, Proj> >
Comp = std::ranges::less>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> &&
std::indirectly_copyable_storable< std::ranges::iterator_t<R>,
std::ranges::range_value_t<R>* >
std::ranges::range_value_t<R>
min (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});

// max
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
std::indirect_strict_weak_order< std::projected<std::ranges::iterator_t<R>, Proj> >
Comp = std::ranges::less>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> &&
std::indirectly_copyable_storable< std::ranges::iterator_t<R>,
std::ranges::range_value_t<R>* >
std::ranges::range_value_t<R>
max (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});


// minmax
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
std::indirect_strict_weak_order< std::projected<std::ranges::iterator_t<R>, Proj> >
Comp = std::ranges::less>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> &&
std::indirectly_copyable_storable< std::ranges::iterator_t<R>,
std::ranges::range_value_t<R>* >
std::ranges::minmax_result<std::ranges::range_value_t<R>>
minmax (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});

// min_element
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
@@ -184,6 +244,16 @@ Element Search Operations
std::ranges::borrowed_iterator_t<R>
max_element (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});

// minmax_element
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
std::indirect_strict_weak_order< std::projected<std::ranges::iterator_t<R>, Proj> >
Comp = std::ranges::less>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R>
std::ranges::minmax_element_result<std::ranges::borrowed_iterator_t<R>>
minmax_element (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});

}

Sequence Search and Comparison
@@ -206,6 +276,32 @@ Sequence Search and Comparison
bool equal (ExecutionPolicy&& pol, R1&& r1, R2&& r2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {});

// mismatch
template<typename ExecutionPolicy, std::ranges::random_access_range R1,
std::ranges::random_access_range R2, typename Pred = std::ranges::equal_to,
typename Proj1 = std::identity, typename Proj2 = std::identity>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
(std::ranges::sized_range<R1> || std::ranges::sized_range<R2>) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to keep it like that? Or are we going to make a change to &&? I think we had this conversation but I don't remember what we ended up with...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will keep it like that, it's described in the differences - see https://github.com/uxlfoundation/oneAPI-spec/pull/610/files

std::indirectly_comparable< std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>,
Pred, Proj1, Proj2 >
std::ranges::mismatch_result<std::ranges::borrowed_iterator_t<R1>,
std::ranges::borrowed_iterator_t<R2>>
mismatch (ExecutionPolicy&& pol, R1&& r1, R2&& r2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {});


// find_end
template<typename ExecutionPolicy, std::ranges::random_access_range R1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I think we need to introduce execution-policy exposition-only concept at some point (not in this PR) to just simplify the specification. We can do it at any point because I don't consider it a breaking change anyhow

std::ranges::random_access_range R2, typename Pred = std::ranges::equal_to,
typename Proj1 = std::identity, typename Proj2 = std::identity>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R1> && std::ranges::sized_range<R2> &&
std::indirectly_comparable< std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>,
Pred, Proj1, Proj2 >
std::ranges::borrowed_subrange_t<R1>
find_end (ExecutionPolicy&& pol, R1&& r1, R2&& r2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {});

// search
template<typename ExecutionPolicy, std::ranges::random_access_range R1,
std::ranges::random_access_range R2, typename Pred = std::ranges::equal_to,
@@ -265,6 +361,16 @@ Sorting and Merge
std::ranges::sized_range<R>
bool is_sorted (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});

// is_sorted_until
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
std::indirect_strict_weak_order< std::projected<std::ranges::iterator_t<R>, Proj> >
Comp = std::ranges::less>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R>
std::ranges::borrowed_iterator_t<R>
is_sorted_until (ExecutionPolicy&& pol, R&& r, Comp comp = {}, Proj proj = {});

// merge
template <typename ExecutionPolicy, std::ranges::random_access_range R1,
std::ranges::random_access_range R2, std::ranges::random_access_range OutR,
@@ -283,8 +389,8 @@ Sorting and Merge

}

Mutating Operations
+++++++++++++++++++
Copying Mutating Operations
+++++++++++++++++++++++++++

.. code:: cpp

@@ -313,6 +419,16 @@ Mutating Operations
std::ranges::borrowed_iterator_t<OutR>>
copy_if (ExecutionPolicy&& pol, R&& r, OutR&& result, Pred pred, Proj proj = {});

// move
template <typename ExecutionPolicy, std::ranges::random_access_range R,
std::ranges::random_access_range OutR>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> && std::ranges::sized_range<OutR> &&
std::indirectly_movable<std::ranges::iterator_t<R>, std::ranges::iterator_t<OutR>>
std::ranges::move_result<std::ranges::borrowed_iterator_t<R>,
std::ranges::borrowed_iterator_t<OutR>>
move (ExecutionPolicy&& pol, R&& r, OutR&& result);

// transform (unary)
template <typename ExecutionPolicy, std::ranges::random_access_range R,
std::ranges::random_access_range OutR, std::copy_constructible Fn,
@@ -344,5 +460,71 @@ Mutating Operations

}

In-place Mutating Operations
++++++++++++++++++++++++++++

.. code:: cpp

// Defined in <oneapi/dpl/algorithm>

namespace oneapi::dpl::ranges {

// fill
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename T = std::ranges::range_value_t<R>>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> &&
std::indirectly_writable<std::ranges::iterator_t<R>, const T&>
std::ranges::borrowed_iterator_t<R>
fill (ExecutionPolicy&& pol, R&& r, const T& value);

// replace
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
typename T1 = /*projected-value-type*/<std::ranges::iterator_t<R>, Proj>, typename T2 = T1>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> &&
std::indirectly_writable<std::ranges::iterator_t<R>, const T2&> &&
std::indirect_binary_predicate< std::ranges::equal_to,
std::projected<std::ranges::iterator_t<R>, Proj>,
const T1* >
std::ranges::borrowed_iterator_t<R>
replace (ExecutionPolicy&& pol, R&& r, const T1& old_value, const T2& new_value,
Proj proj = {});

// replace_if
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
typename T = /*projected-value-type*/<std::ranges::iterator_t<R>, Proj>,
std::indirect_unary_predicate< std::projected<std::ranges::iterator_t<R>, Proj> > Pred>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> &&
std::indirectly_writable<std::ranges::iterator_t<R>, const T&>
std::ranges::borrowed_iterator_t<R>
replace_if (ExecutionPolicy&& pol, R&& r, Pred pred, const T& new_value, Proj proj = {});

// remove
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
typename T = /*projected-value-type*/<std::ranges::iterator_t<R>, Proj>>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> && std::permutable<std::ranges::iterator_t<R> &&
std::indirect_binary_predicate< std::ranges::equal_to,
std::projected<std::ranges::iterator_t<R>, Proj>,
const T* >
std::ranges::borrowed_subrange_t<R>
remove (ExecutionPolicy&& pol, R&& r, const T& value, Proj proj = {});

// remove_if
template <typename ExecutionPolicy, std::ranges::random_access_range R,
typename Proj = std::identity,
std::indirect_unary_predicate< std::projected<std::ranges::iterator_t<R>, Proj> > Pred>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> &&
std::ranges::sized_range<R> && std::permutable<std::ranges::iterator_t<R>>
std::ranges::borrowed_subrange_t<R>
remove_if (ExecutionPolicy&& pol, R&& r, Pred pred, Proj proj = {});

}

.. _`C++ Standard`: https://isocpp.org/std/the-standard
.. _`SYCL`: https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html