Skip to content

Commit 1304c58

Browse files
authored
[oneTBB] Improve rvalue support in parallel_reduce and parallel_for_each (#647)
* Improve named requirements to better deal with value categories * Clarify the use of pseudo-signatures
1 parent 870e9e5 commit 1304c58

File tree

7 files changed

+102
-70
lines changed

7 files changed

+102
-70
lines changed

source/elements/oneTBB/source/algorithms/functions/parallel_for_each_func.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ Function template that processes work items in parallel.
3737
Requirements:
3838

3939
* The ``Body`` type must meet the :doc:`ParallelForEachBody requirements <../../named_requirements/algorithms/par_for_each_body>`.
40-
Since C++17, ``Body`` may also be a pointer to a member function in ``Index``.
40+
Since C++17, ``Body`` may also be a pointer to a member function in ``std::iterator_traits<InputIterator>::value_type``.
4141
* The ``InputIterator`` type must meet the `Input Iterator` requirements from the [input.iterators] section of the ISO C++ Standard.
4242
* If ``InputIterator`` type does not meet the `Forward Iterator` requirements from the [forward.iterators] section of the ISO C++ Standard,
4343
the ``std::iterator_traits<InputIterator>::value_type`` type must be constructible from ``std::iterator_traits<InputIterator>::reference``.
4444
* The ``Container`` type must meet the :doc:`ContainerBasedSequence requirements <../../named_requirements/algorithms/container_based_sequence>`.
45-
* The type returned by ``Container::begin()`` must meet the same requirements as the ``InputIterator`` type above.
45+
* The type returned by ``std::begin`` and ``std::end`` applied to a ``Container`` object
46+
must meet the same requirements as the ``InputIterator`` type above.
4647

4748
The ``parallel_for_each`` template has two forms.
4849

source/elements/oneTBB/source/named_requirements.rst

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ Named Requirements
1010
This section describes named requirements used in the oneTBB Specification.
1111

1212
A *named requirement* is a set of requirements on a type. The requirements may be syntactic or semantic.
13-
The *named_requirement* term is similar to “Requirements on types and expressions” term which is defined
14-
by the ISO C++ Standard (chapter “Library Introduction”) or `“Named Requirements” section <https://en.cppreference.com/w/cpp/named_req>`_
15-
on the cppreference.com site.
13+
The *named requirement* term is similar to “Requirements on types and expressions” term which is defined
14+
by the ISO C++ Standard (chapter “Library Introduction”) or
15+
`“Named Requirements” section <https://en.cppreference.com/w/cpp/named_req>`_ on the cppreference.com site.
1616

1717
For example, the named requirement of *sortable* could be defined as a set of requirements that enable
1818
an array to be sorted. A type ``T`` would be *sortable* if:
@@ -22,11 +22,22 @@ an array to be sorted. A type ``T`` would be *sortable* if:
2222

2323
You can write a sorting template function in C++ that sorts an array of any type that is *sortable*.
2424

25+
.. _pseudo_signatures:
26+
27+
Pseudo-Signatures
28+
-----------------
29+
2530
Two approaches for defining named requirements are *valid expressions* and *pseudo-signatures*.
26-
The ISO C++ standard follows the valid *expressions* approach, which shows what the usage pattern looks like for a requirement.
27-
It has the drawback of relegating important details to notational conventions. This document uses
31+
The ISO C++ standard follows the *valid expressions* approach, which shows what the usage pattern looks like for a requirement.
32+
It has the drawback of relegating important details to notational conventions. This document mostly uses
2833
pseudo-signatures because they are concise and can be cut-and-pasted for an initial implementation.
2934

35+
A pseudo-signature describes how an implementation interacts with a type or a function.
36+
A real function signature (after template instantiation, if applicable) may differ from the pseudo-signature
37+
that it implements in ways where implicit conversions would deal with the difference,
38+
transforming function parameter types from the ones in the pseudo-signature to the real signature,
39+
and transforming the actual return value type to the one in the pseudo-signature.
40+
3041
For example, the table below shows pseudo-signatures for a *sortable* type ``T``:
3142

3243
---------------------------------------------------------------------------------------------
@@ -35,20 +46,49 @@ For example, the table below shows pseudo-signatures for a *sortable* type ``T``
3546

3647
.. cpp:function:: bool operator<(const T& x, const T& y)
3748

38-
Compare x and y.
49+
Compare ``x`` and ``y``.
3950

4051
.. cpp:function:: void swap(T& x, T& y)
4152

42-
Swap x and y.
53+
Swap ``x`` and ``y``.
4354

4455
---------------------------------------------------------------------------------------------
4556

46-
A real signature may differ from the pseudo-signature that it implements in ways where implicit
47-
conversions would deal with the difference. For an example type ``U``, the real signature that
48-
implements ``operator<`` in the table above can be expressed as ``int operator<( U x, U y )``,
49-
because C++ permits implicit conversion from ``int`` to ``bool``, and implicit conversion from ``U``
50-
to (``const U&``). Similarly, the real signature ``bool operator<( U& x, U& y )`` is acceptable
51-
because C++ permits implicit addition of a const qualifier to a reference type.
57+
For a given type ``U``, the real signature that implements ``operator<`` in the table above
58+
can be expressed as ``int operator<( U x, U y )``, because C++ permits implicit conversion from
59+
``int`` to ``bool``, and implicit conversion from ``const U&`` to ``U`` if the type is copy-constructible.
60+
As a counter-example, the real signature ``bool operator<( U& x, U& y )`` is not acceptable
61+
because C++ does not permit implicit removal of the ``const`` qualifier from a type, and so the code
62+
would not compile if the implementation attempts to pass a constant object to the function.
63+
64+
Besides pseudo-signatures, semantic requirements also need to be met by real types and functions.
65+
For example, while ``std::pair<U, U> swap(U x, U y)`` fits the pseudo-signature for *Sortable*
66+
via implicit conversion of references to values and implicit drop of the returned value
67+
(ignored by a library implementation), it is unable to swap the actual variables passed to the function
68+
and therefore does not meet the semantic requirements of *Sortable*.
69+
70+
The following table provides guidance for the types of parameters used in pseudo-signatures
71+
and potential alternatives in real signatures. In practice, suitable alternatives might depend
72+
on the semantic requirements as well as type properties, such as availability of copy- or move-constructors.
73+
74+
========================== ================================ =============================
75+
Pseudo-signature parameter General semantics Alternative real parameters
76+
========================== ================================ =============================
77+
``const T& a`` The function is not supposed - ``T a``
78+
to modify the argument. - ``X& a``, ``auto& a``
79+
- ``X&& a``, ``auto&& a``
80+
81+
Here and below ``X`` is a template type parameter.
82+
83+
``T& a`` The argument is an lvalue. - ``const T& a``
84+
The function can or is - ``T a``
85+
supposed to modify the argument. - ``X& a``, ``auto& a``
86+
- ``X&& a``, ``auto&& a``
87+
88+
``T&& a`` The argument is an rvalue. The - ``const T& a``
89+
function can use the argument - ``T a``
90+
as a source in move operations. - ``X&& a``, ``auto&& a``
91+
========================== ================================ =============================
5292

5393
Algorithms
5494
----------
@@ -81,7 +121,6 @@ Mutexes
81121

82122
Containers
83123
----------
84-
85124
.. toctree::
86125
:titlesonly:
87126

source/elements/oneTBB/source/named_requirements/algorithms/container_based_sequence.rst

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,27 @@ ContainerBasedSequence
77
======================
88
**[req.container_based_sequence]**
99

10-
A type `C` satisfies `ContainerBasedSequence` if it meets the following requirements:
10+
A type `C` satisfies `ContainerBasedSequence` if the following expressions are valid
11+
for a (possibly const) object ``c`` of the type `C`:
1112

1213
----------------------------------------------------------------
1314

14-
**ContainerBasedSequence Requirements: Pseudo-Signature, Semantics**
15+
**ContainerBasedSequence Requirements: Expression, Semantics**
1516

16-
.. note::
17+
.. cpp:function:: std::begin(c)
1718

18-
In this page ``c`` is an object of type (possibly ``const``) ``C``.
19+
Returns an iterator to the beginning of the sequence represented by ``c``.
1920

20-
Templates that use the named requirement can impose stricter requirements on the iterator concept.
21+
.. cpp:function:: std::end(c)
2122

22-
.. cpp:function:: std::begin(c)
23+
Returns an iterator one past the end of the sequence represented by ``c``.
2324

24-
Returns an input iterator to the beginning of the sequence represented by ``c``.
25+
----------------------------------------------------------------
2526

26-
.. cpp:function:: std::end(c)
27+
.. note::
2728

28-
Returns an input iterator one past the end of the sequence represented by ``c``.
29+
The category of an iterator returned by ``std::begin``/``std::end`` is determined by
30+
a template that uses `ContainerBasedSequence`.
2931

3032
See also:
3133

source/elements/oneTBB/source/named_requirements/algorithms/par_for_each_body.rst

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,54 +7,44 @@ ParallelForEachBody
77
===================
88
**[req.parallel_for_each_body]**
99

10-
A type `Body` satisfies `ParallelForBody` if it meets the `Function Objects`
11-
requirements described in the [function.objects] section of the ISO C++ standard.
12-
It should also meet one of the following requirements:
10+
A type `Body` satisfies `ParallelForEachBody` if it meets the `Function Objects`
11+
requirements described in the [function.objects] section of the ISO C++ standard,
12+
as well as meets exactly one of the following two alternative requirements for ``operator()``:
1313

1414
----------------------------------------------------------------
1515

1616
**ParallelForEachBody Requirements: Pseudo-Signature, Semantics**
1717

18-
.. cpp:function:: Body::operator()( ItemType item ) const
18+
Alternative 1:
1919

20-
Process the received item.
21-
22-
.. cpp:function:: Body::operator()( ItemType item, oneapi::tbb::feeder<ItemType>& feeder ) const
23-
24-
Process the received item. May invoke the ``feeder.add(T)`` function to spawn additional items.
25-
26-
-----------------------------------------------------------------
20+
.. cpp:function:: void Body::operator()( ReferenceType item ) const
2721

28-
.. note::
22+
Process the received item.
2923

30-
``ItemType`` may be optionally passed to ``Body::operator()`` by reference.
31-
``const`` and ``volatile`` type qualifiers are also applicable.
24+
----------------------------------------------------------------
3225

33-
Terms
34-
-----
26+
Alternative 2:
3527

36-
* ``iterator`` determines the type of the iterator passed into the ``parallel_for_each`` algorithm,
37-
which is ``decltype(std::begin(c))`` for the overloads that accept the ``Container`` template argument or ``InputIterator``.
38-
* ``value_type`` - the type ``std::iterator_traits<iterator>::value_type``.
39-
* ``reference`` - the type ``std::iterator_traits<iterator>::reference``.
28+
.. cpp:function:: void Body::operator()( ReferenceType item, oneapi::tbb::feeder<ItemType>& feeder ) const
29+
void Body::operator()( ItemType&& item, oneapi::tbb::feeder<ItemType>& feeder ) const
4030

41-
``oneapi::tbb::parallel_for_each`` requires the ``Body::operator()`` call with an object of the ``reference`` type to be well-formed if
42-
the ``iterator`` meets the `Forward iterator` requirements described in the [forward.iterators] section of the
43-
ISO C++ Standard.
31+
Process the received item. May invoke the ``feeder.add`` function to spawn additional items.
32+
The ``Body::operator()`` must accept both ``ReferenceType`` and ``ItemType&&`` values as the first argument.
4433

45-
`oneapi::tbb::parallel_for_each algorithm <../../algorithms/functions/parallel_for_each_func>`_
46-
requires the ``Body::operator()`` call with an object of type ``const value_type&`` or ``value_type&&`` to be well-formed if following requirements are met:
34+
-----------------------------------------------------------------
4735

48-
* the iterator meets the `Input iterator` requirements described in the [input.iterators] section of the ISO C++ Standard
49-
* the iterator does not meet the `Forward iterator` requirements described in the [forward.iterators] section of the ISO C++ Standard
36+
where
5037

51-
.. caution::
38+
* ``ItemType`` is ``std::iterator_traits<Iterator>::value_type`` for the type of the iterator
39+
the ``parallel_for_each`` algorithm operates with, and
40+
* ``ReferenceType`` is ``std::iterator_traits<Iterator>::reference`` if the iterator type is
41+
a `forward iterator` as described in the [forward.iterators] section of the ISO C++ Standard,
42+
* otherwise, ``ReferenceType`` is ``ItemType&&``.
5243

53-
If the ``Body`` only takes non-const lvalue reference to the ``value_type``, the requirements described above
54-
are violated, and the program can be ill-formed.
44+
.. note::
5545

56-
Additional elements submitted into ``oneapi::tbb::parallel_for_each`` through the ``feeder::add`` are passed to the ``Body`` as rvalues. In this case, the corresponding
57-
execution of the ``Body`` is required to be well-formed.
46+
The usual rules for :ref:`pseudo-signatures <pseudo_signatures>` apply.
47+
Therefore, ``Body::operator()`` may optionally take items by value or by ``const`` reference.
5848

5949
See also:
6050

source/elements/oneTBB/source/named_requirements/algorithms/par_reduce_body.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ A type `Body` satisfies `ParallelReduceBody` if it meets the following requireme
3434
See also:
3535

3636
* :doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>`
37-
* :doc:`parallel_determinstic_reduce algorithm <../../algorithms/functions/parallel_deterministic_reduce_func>`
37+
* :doc:`parallel_deterministic_reduce algorithm <../../algorithms/functions/parallel_deterministic_reduce_func>`

source/elements/oneTBB/source/named_requirements/algorithms/par_reduce_func.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
==================
66
ParallelReduceFunc
77
==================
8-
**[req.parallel_reduce_body]**
8+
**[req.parallel_reduce_func]**
99

1010
A type `Func` satisfies `ParallelReduceFunc` if it meets the following requirements:
1111

1212
-----------------------------------------------------------------------------------------------------
1313

1414
**ParallelReduceFunc Requirements: Pseudo-Signature, Semantics**
1515

16-
.. cpp:function:: Value Func::operator()(const Range& range, const Value& x) const
16+
.. cpp:function:: Value Func::operator()(const Range& range, Value&& x) const
1717

18-
Accumulates result for a subrange, starting with initial value ``x``.
19-
``Range`` type must meet the :doc:`Range requirements <range>`.
20-
``Value`` type must be the same as a corresponding template parameter for the
21-
:doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>` algorithm.
18+
Accumulates values over ``range``, starting with the initial value ``x``.
19+
The ``Range`` type must meet the :doc:`Range requirements <range>`.
20+
The ``Value`` type must be the same as the corresponding template parameter for the
21+
:doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>`.
2222

2323
See also:
2424

2525
* :doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>`
26-
* :doc:`parallel_determinstic_reduce algorithm <../../algorithms/functions/parallel_deterministic_reduce_func>`
26+
* :doc:`parallel_deterministic_reduce algorithm <../../algorithms/functions/parallel_deterministic_reduce_func>`

source/elements/oneTBB/source/named_requirements/algorithms/par_reduce_reduction.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ A type `Reduction` satisfies `ParallelReduceReduction` if it meets the following
1313

1414
**ParallelReduceReduction Requirements: Pseudo-Signature, Semantics**
1515

16-
.. cpp:function:: Value Reduction::operator()(const Value& x, const Value& y) const
16+
.. cpp:function:: Value Reduction::operator()(Value&& x, Value&& y) const
1717

18-
Combines results ``x`` and ``y``.
19-
``Value`` type must be the same as a corresponding template parameter for the
20-
:doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>` algorithm.
18+
Combines the results ``x`` and ``y``.
19+
The ``Value`` type must be the same as the corresponding template parameter for the
20+
:doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>`.
2121

2222
See also:
2323

2424
* :doc:`parallel_reduce algorithm <../../algorithms/functions/parallel_reduce_func>`
25-
* :doc:`parallel_determinstic_reduce algorithm <../../algorithms/functions/parallel_deterministic_reduce_func>`
25+
* :doc:`parallel_deterministic_reduce algorithm <../../algorithms/functions/parallel_deterministic_reduce_func>`

0 commit comments

Comments
 (0)