Skip to content
26 changes: 20 additions & 6 deletions source/elements/oneDPL/source/parallel_api/buffer_wrappers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@
Buffer Wrappers
---------------

A *buffer wrapper* is an iterator-like type for processing data in `SYCL`_ buffers with oneDPL algorithms.
It is either the type of a *buffer position object* returned by
``oneapi::dpl::begin`` or ``oneapi::dpl::end`` or a type composed of at least one buffer position object,
with the same capabilities and limitations as buffer position objects.
Buffer wrappers can be used as source types for :doc:`oneDPL iterators <iterators>`, and the resulting type is also a
buffer wrapper.

Buffer wrappers substitute for iterators as arguments and return values of oneDPL algorithms, however they cannot be
used as iterators in other contexts, including dereference, as these types do not fully satisfy the C++ standard
requirements for an iterator.

Buffer Position Objects
-----------------------

.. code:: cpp

// Defined in <oneapi/dpl/iterator>
Expand Down Expand Up @@ -48,14 +62,18 @@ which type satisfies the following requirements but is otherwise unspecified:

- It is copy-constructible and copy-assignable.
- It is comparable with ``operator==`` and ``operator!=``.
- It provides the ``get_buffer()`` method that for a buffer position object returns the SYCL buffer,
which the object was built over.
- The expressions ``a + n`` and ``a - n``, where ``a`` is a buffer position object and ``n``
is an integer value, are valid and evaluate to a buffer position object such that
the corresponding position in the buffer follows (precedes) that of ``a`` by ``n``.
If this new position is out of the buffer bounds, the behavior is undefined.
- The expression ``a - b``, where ``a`` and ``b`` are buffer position objects,
is valid and evaluates to an integer value ``n`` such that ``a == b + n``.
- It provides the ``get_buffer()`` method that for a buffer position object returns the SYCL buffer,
which the object was built over.

.. note::
Only buffer position objects are required to support the ``get_buffer()`` method, other buffer wrappers
composed of buffer position objects are not required to support this method.

If these operators and expressions are applied to buffer position objects that are not built
over the same SYCL buffer, the behavior is undefined.
Expand All @@ -73,10 +91,6 @@ in expressions with other objects built over the same buffer.
auto pos = dpl::find(dpl::execution::dpcpp_default, dpl::begin(buf), dpl::end(buf), value);
int value_index = (pos == dpl::end(buf)) ? -1 : (pos - dpl::begin(buf));

.. note::
Though buffer position objects substitute for iterators as arguments and return values
of oneDPL algorithms, they cannot be used as iterators in other contexts, including dereference,
as their type does not satisfy the C++ standard requirements for an iterator.

SYCL deduction tags (the ``TagT`` parameters) and ``sycl::property::no_init``
allow to specify an access mode to be used by algorithms for accessing a SYCL buffer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ device_policy Class

An object of the ``device_policy`` type is associated with a ``sycl::queue`` that is used
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
it is capable of processing SYCL buffers (passed via ``oneapi::dpl::begin/end`` or other
:doc:`buffer wrappers <buffer_wrappers>`), 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, 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.
Expand Down
22 changes: 16 additions & 6 deletions source/elements/oneDPL/source/parallel_api/iterators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ iterators. However, they are indirectly device accessible because they represent
When passed to oneDPL algorithms with a ``device_policy``, indirectly device accessible iterator types that are also
random access iterators and satisfy *SYCL device-copyable* must not cause unnecessary data movement beyond what is
required by the algorithm's semantics and what would be required to use the type directly within a SYCL kernel.
Indirectly device accessible buffer position objects must not cause unnecessary data movement beyond what is
required by the algorithm's semantics and what would be required by using an accessor to the buffer within a SYCL
kernel.

Similarly, when passed to oneDPL algorithms with a ``device_policy``, buffer position objects must not cause
unnecessary data movement beyond what is required by the algorithm's semantics and what would be required
by using an accessor to the buffer within a SYCL kernel.

The same requirement to prevent excessive data movements equally applies to indirectly device accessible types
composed of one or more source components, such as the oneDPL iterators.

Indirect Device Accessibility Type Trait
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -52,17 +56,20 @@ oneDPL Iterators
The oneDPL iterators are defined in the ``<oneapi/dpl/iterator>`` header,
in ``namespace oneapi::dpl``.

Let us define a named requirement, ``AdaptingIteratorSource``, to describe valid random access iterator-like
Let us define a named requirement, ``AdaptingIteratorSource``, to describe random access iterator-like
types that can be used as source for oneDPL iterators as described below.
The type ``Iter`` satisfies the ``AdaptingIteratorSource`` named requirement if it is any of the following:

* A random access iterator
* The unspecified iterator-like type returned by ``oneapi::dpl::begin`` or ``oneapi::dpl::end``
* A ``permutation_iterator``
* A ``transform_iterator``
* A ``counting_iterator``
* A ``discard_iterator``
* A ``zip_iterator``
* A :doc:`buffer position object <buffer_wrappers>` type returned by ``oneapi::dpl::begin`` and ``oneapi::dpl::end``

If ``AdaptingIteratorSource`` is a buffer wrapper, a type built upon that also becomes a buffer wrapper,
not an iterator, and cannot be directly dereferenced (see :doc:`Buffer wrappers <buffer_wrappers>`).

.. code:: cpp

Expand Down Expand Up @@ -209,12 +216,15 @@ algorithm with a ``device_policy``, ``SourceIterator`` must be indirectly device
The type ``IndexMap`` must be one of the following:

* A random access iterator
* The unspecified iterator-like type returned by ``oneapi::dpl::begin`` or ``oneapi::dpl::end``
* A ``permutation_iterator``
* A ``transform_iterator``
* A ``counting_iterator``
* A functor with a signature equivalent to ``T operator()(const T&) const`` where ``T`` is a
``std::iterator_traits<SourceIterator>::difference_type``
* A :doc:`buffer position object <buffer_wrappers>` type returned by ``oneapi::dpl::begin`` and ``oneapi::dpl::end``

If the ``IndexMap`` is a buffer wrapper, the ``permutation_iterator`` built upon that will be a buffer wrapper, not an
iterator, and cannot be directly dereferenced (see :doc:`Buffer wrappers <buffer_wrappers>`).

``permutation_iterator::operator*`` uses the counter value of the instance on which
it is invoked to index into the index map. The corresponding value in the map is then used
Expand Down
Loading