Skip to content

[oneDPL] Indirectly Device Accessible Iterator Customization Point and Public Trait #620

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

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d90682b
Initial draft
danhoeflinger Apr 3, 2025
24b879d
grammar, clarity
danhoeflinger Apr 3, 2025
ff940e8
formatting
danhoeflinger Apr 3, 2025
0c0804b
code indentation
danhoeflinger Apr 4, 2025
8cff3e4
testing indentation formatting
danhoeflinger Apr 4, 2025
a36a006
indentation of comments in code
danhoeflinger Apr 4, 2025
cb102cb
removing unnecessary const ref
danhoeflinger Apr 4, 2025
bc32ce2
more indentation changes
danhoeflinger Apr 4, 2025
e7ffb99
language improvements
danhoeflinger Apr 4, 2025
913d555
remove some repetition
danhoeflinger Apr 4, 2025
29e209f
fix underline
danhoeflinger Apr 4, 2025
180a500
improve example, shorten var names
danhoeflinger Apr 4, 2025
a852f0f
improve comment clarity
danhoeflinger Apr 4, 2025
41b9828
Adding link for SYCL
danhoeflinger Apr 4, 2025
96b1b45
Device accessible content instead of passed directly
danhoeflinger Apr 4, 2025
dd71fdf
more information about device policy iterator compatibility
danhoeflinger Apr 4, 2025
21b8d0f
language improvement
danhoeflinger Apr 4, 2025
7c76593
readjusting content
danhoeflinger Apr 4, 2025
250aed2
minor improvements
danhoeflinger Apr 4, 2025
696d925
Improve language
danhoeflinger Apr 4, 2025
b250e77
accept suggestion
danhoeflinger Apr 7, 2025
8928a06
removing legacy passed direcly description
danhoeflinger Apr 7, 2025
1fff43d
Adjusting structure, and some language improvements
danhoeflinger Apr 7, 2025
59c147a
remove "always"
danhoeflinger Apr 8, 2025
0fe3101
Address feedback, take suggestions
danhoeflinger Apr 8, 2025
1d44b35
adding template to the text
danhoeflinger Apr 8, 2025
a918a45
remove usm std::vector::iterators mention
danhoeflinger Apr 8, 2025
25edb0b
adjust code formatting
danhoeflinger Apr 8, 2025
b4aeaf0
Signed-off-by: Dan Hoeflinger <[email protected]>
danhoeflinger Apr 8, 2025
4967f43
are -> is
danhoeflinger Apr 9, 2025
583fd52
language adjustment for base characteristic;
danhoeflinger Apr 9, 2025
b8a6018
rename to "indirectly device accessible iterators"
danhoeflinger Apr 9, 2025
311f2cb
formatting
danhoeflinger Apr 9, 2025
8676878
Adding a section for other iterators
danhoeflinger Apr 10, 2025
ef67c3d
remove unnecessary implementation details
danhoeflinger Apr 11, 2025
0f64be0
trait<buffer wrapper> = true
danhoeflinger Apr 11, 2025
780f501
restricting permutation_iterator SourceIterator to indirectly device …
danhoeflinger Apr 11, 2025
a57c7d7
formatting
danhoeflinger Apr 11, 2025
6114c2c
language improvements
danhoeflinger Apr 16, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ to run algorithms on a SYCL device. When an algorithm runs with ``device_policy`
it is capable of processing SYCL buffers (passed via ``oneapi::dpl::begin/end``),
data in the host memory and data in Unified Shared Memory (USM), including device USM.
Data placed in the host memory and USM can be passed to oneDPL algorithms
as pointers and random access iterators. The way to transfer data from the host memory
to a device and back is unspecified; per-element data movement to/from a temporary storage
is a possible valid implementation.
as pointers and random access iterators, including :doc:`oneDPL iterators <iterators>`. The way to transfer data from
the host memory to a device and back is unspecified; per-element data movement to/from a temporary storage is a possible
valid implementation.

The ``KernelName`` template parameter, also aliased as ``kernel_name`` within the class template,
is to explicitly provide a name for SYCL kernels executed by an algorithm the policy is passed to.
Expand Down
141 changes: 141 additions & 0 deletions source/elements/oneDPL/source/parallel_api/iterators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,31 @@
..
.. SPDX-License-Identifier: CC-BY-4.0

.. _iterators:

Iterators
---------

Requirements For Iterator Use With Device Policies
++++++++++++++++++++++++++++++++++++++++++++++++++
Iterator are, by default, not assumed to refer to content which is accessible on the device which requires that content
to be copied to the device prior to being used inside a `SYCL`_ kernel. We define iterators to be "indirectly device
accessible iterators" when they can inherently be dereferenced on the device in a SYCL kernel. When using oneDPL
algorithms with a ``device_policy``, "indirectly device accessible iterators" avoid unnecessary data movement when
provided as input or output arguments.

Examples of "indirectly device accessible iterators" include SYCL USM shared or device memory, or iterator types like
``counting_iterator`` or ``discard_iterator`` that do not require any data to be copied to the device to be used in a
SYCL kernel. An example of an iterator which does not refer to "indirectly device accessible" is a ``std::vector``
iterator, which requires the data to be copied to the device in some way prior to usage in a SYCL kernel within
algorithms used with a ``device_policy``.

When used as input or output with algorithms using a ``device_policy`` "indirectly device accessible iterators" must
also be SYCL device-copyable, as defined by the SYCL Specification.

oneDPL Iterators
++++++++++++++++

The oneDPL iterators are defined in the ``<oneapi/dpl/iterator>`` header,
in ``namespace oneapi::dpl``.

Expand Down Expand Up @@ -64,6 +86,8 @@ counter; dereference operations cannot be used to modify the counter. The arithm
operators of ``counting_iterator`` behave as if applied to the values of Integral type
representing the counters of the iterator instances passed to the operators.

``counting_iterator`` is SYCL device-copyable, and is an "indirectly device accessible iterator".

.. code:: cpp

class discard_iterator
Expand Down Expand Up @@ -104,6 +128,8 @@ lvalue that may be assigned an arbitrary value. The assignment has no effect on
of ``discard_iterator`` behave as if applied to integer counter values maintained by the
iterator instances to determine their position relative to each other.

``discard_iterator`` is SYCL device-copyable, and is an "indirectly device accessible iterator".

.. code:: cpp

template <typename SourceIterator, typename IndexMap>
Expand Down Expand Up @@ -174,6 +200,10 @@ to index into the index map. The corresponding value in the map is then used
to index into the value set defined by the source iterator. The resulting lvalue is returned
as the result of the operator.

``permutation_iterator`` is SYCL device-copyable if both the ``SourceIterator`` and the ``IndexMap``
are SYCL device-copyable. ``permutation_iterator`` is an "indirectly device accessible iterator" if both the
``SourceIterator`` and the ``IndexMap`` are "indirectly device accessible iterators".

.. code:: cpp

template <typename SourceIterator, typename IndexMap>
Expand Down Expand Up @@ -234,6 +264,9 @@ arithmetic and comparison operators of ``transform_iterator`` behave as if appli
source iterator itself. The template type ``Iterator`` must satisfy
``AdaptingIteratorSource``.

``transform_iterator`` is SYCL device-copyable if the source iterator is SYCL device-copyable, and
is "indirectly device accessible iterator" if the source iterator is an "indirectly device accessible iterators".

.. code:: cpp

template <typename UnaryFunc, typename Iterator>
Expand Down Expand Up @@ -293,6 +326,9 @@ source iterators over which the ``zip_iterator`` is defined. The arithmetic oper
operation were applied to each of these iterators. The types ``T`` within the template pack
``Iterators...`` must satisfy ``AdaptingIteratorSource``.

``zip_iterator`` is SYCL device-copyable if all the source iterators are SYCL device-copyable, and
is an "indirectly device accessible iterator" if all the source iterators are "indirectly device accessible iterators".

.. code:: cpp

template <typename... Iterators>
Expand All @@ -301,3 +337,108 @@ operation were applied to each of these iterators. The types ``T`` within the te

``make_zip_iterator`` constructs and returns an instance of ``zip_iterator``
using the set of source iterators provided.

.. _iterators-device-accessible:

Customization For User Defined Iterators
++++++++++++++++++++++++++++++++++++++++

oneDPL provides a mechanism to indicate whether custom iterators are "indirectly device accessible iterators" by
defining an Argument-Dependent Lookup (ADL) based customization point, ``is_onedpl_indirectly_device_accessible``.
oneDPL also defines a public trait, ``is_indirectly_device_accessible[_v]`` to indicate whether an iterator is an
"indirectly device accessible iterators".

oneDPL queries this information at compile time to determine how to handle iterator types when they are passed to
algorithms with a ``device_policy`` to avoid unnecessary data movement.

ADL Customization Point: ``is_onedpl_indirectly_device_accessible``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A free function ``is_onedpl_indirectly_device_accessible(IteratorT)`` may be defined, which accepts an argument
of type ``IteratorT`` and returns a type with the base characteristic of ``std::true_type`` if ``IteratorT`` refers to
"indirectly device accessible", or otherwise returns a type with the base characteristic of ``std::false_type``. The
function must be discoverable by ADL.

The function ``is_onedpl_indirectly_device_accessible`` may be used by oneDPL to determine if the iterator refers
to "indirectly device accessible" by interrogating its return type at compile-time only. Overloads may be provided as
forward declarations only, without a body defined. ADL is used to determine which function overload to use according to
the rules in the `C++ Standard`_. Therefore, derived iterator types without an overload for their exact type will match
their most specific base iterator type if such an overload exists.

The default implementation of ``is_onedpl_indirectly_device_accessible`` marks the following iterators as to
"indirectly device accessible iterators":
* Pointers (to handle USM pointers)
* ``std::reverse_iterator<IteratorT>`` when ``IteratorT`` refers to "indirectly device accessible"



Public Trait: ``oneapi::dpl::is_indirectly_device_accessible[_v]``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The following class template and variable template are defined in ``<oneapi/dpl/iterator>`` inside the namespace
``oneapi::dpl``:

.. code:: cpp

template <typename T>
struct is_indirectly_device_accessible{ /* see below */ };

template <typename T>
inline constexpr bool is_indirectly_device_accessible_v = is_indirectly_device_accessible<T>::value;

``template <typename T> oneapi::dpl::is_indirectly_device_accessible`` is a type which has the base characteristic
of ``std::true_type`` if ``T`` refers to "indirectly device accessible", otherwise it has the base characteristic of
``std::false_type``.





Example
^^^^^^^

The following example shows how to define a customization for `is_indirectly_device_accessible` trait for a simple
user defined iterator. It also shows a more complicated example where the customization is defined as a hidden friend
of the iterator class.

.. code:: cpp

namespace usr
{
struct accessible_it
{
/* unspecified user definition of a iterator which refers to "indirectly device accessible" */
};

std::true_type
is_onedpl_indirectly_device_accessible(accessible_it);

struct inaccessible_it
{
/* unspecified user definition of iterator which doesn't refer to "indirectly device accessible" */
};

// This could be omitted. It would rely upon the default implementation with the same result
std::false_type
is_onedpl_indirectly_device_accessible(inaccessible_it);
}

static_assert(oneapi::dpl::is_indirectly_device_accessible<usr::accessible_it> == true);
static_assert(oneapi::dpl::is_indirectly_device_accessible<usr::inaccessible_it> == false);

// Example with base iterators and ADL overload as a hidden friend
template <typename It1, typename It2>
struct it_pair
{
It1 first;
It2 second;
friend auto is_onedpl_indirectly_device_accessible(it_pair) ->
std::conjunction<oneapi::dpl::is_indirectly_device_accessible<It1>,
oneapi::dpl::is_indirectly_device_accessible<It2>>;
};

static_assert(oneapi::dpl::is_indirectly_device_accessible<it_pair<usr::accessible_it, usr::accessible_it>> == true);
static_assert(oneapi::dpl::is_indirectly_device_accessible<it_pair<usr::accessible_it, usr::inaccessible_it>> == false);

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