Title
Parallel adjacent_difference shouldn't require creating temporaries
Status
c++20
Section
[adjacent.difference]
Submitter
Billy O'Neal III

Created on 2018-02-02.00:00:00 last changed 45 months ago

Messages

Date: 2018-06-12.01:05:16

Proposed resolution:

This wording is relative to N4713.

  1. Modify [adjacent.difference] as indicated:

    template<class InputIterator, class OutputIterator>
      OutputIterator
        adjacent_difference(InputIterator first, InputIterator last, OutputIterator result);
    template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
      ForwardIterator2
        adjacent_difference(ExecutionPolicy&& exec,
                            ForwardIterator1 first, ForwardIterator1 last, ForwardIterator2 result);
    
    template<class InputIterator, class OutputIterator, class BinaryOperation>
      OutputIterator
        adjacent_difference(InputIterator first, InputIterator last,
                            OutputIterator result, BinaryOperation binary_op);
    template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
             class BinaryOperation>
      ForwardIterator2
        adjacent_difference(ExecutionPolicy&& exec,
                            ForwardIterator1 first, ForwardIterator1 last,
                            ForwardIterator2 result, BinaryOperation binary_op);
    

    -?- Let T be the value type of decltype(first). For the overloads that do not take an argument binary_op, let binary_op be an lvalue that denotes an object of type minus<>.

    -1- Requires:

    1. (1.1) — For the overloads with no ExecutionPolicy, InputIterator’s value type T shall be MoveAssignable (Table 25) and shall be constructible from the type of *first. acc (defined below) shall be writable ([iterator.requirements.general]) to the result output iterator. The result of the expression val - std::move(acc) or binary_op(val, std::move(acc)) shall be writable to the result output iterator.

    2. (1.2) — For the overloads with an ExecutionPolicy, the value type of ForwardIterator1 shall be CopyConstructible (Table 24), constructible from the expression *first - *first or binary_op(*first, *first), and assignable to the value type of ForwardIterator2result of the expressions binary_op(*first, *first) and *first shall be writable to result.

    3. (1.3) — […]

    -2- Effects: For the overloads with no ExecutionPolicy and a non-empty range, the function creates an accumulator acc whose type is InputIterator’s value type of type T, initializes it with *first, and assigns the result to *result. For every iterator i in [first + 1, last) in order, creates an object val whose type is InputIterator’s value type T, initializes it with *i, computes val - std::move(acc) or binary_op(val, std::move(acc)), assigns the result to *(result + (i - first)), and move assigns from val to acc.

    -3- For the overloads with an ExecutionPolicy and a non-empty range, first the function creates an object whose type is ForwardIterator1's value type, initializes it with *first, and assigns the result to *result. Then for every d in [1, last - first - 1], creates an object val whose type is ForwardIterator1's value type, initializes it with *(first + d) - *(first + d - 1) or binary_op(*(first + d), *(first + d - 1)), and assigns the result to *(result + d)performs *result = *first. Then, for every d in [1, last - first - 1], performs *(result + d) = binary_op(*(first + d), *(first + (d - 1))).

Date: 2018-06-12.01:05:16

[ 2018-06 Rapperswil: Adopted ]

Date: 2018-03-14.00:00:00

[ 2018-3-14 Wednesday evening issues processing; remove 'const' before minus and move to Ready. ]

Previous resolution [SUPERSEDED]:

This wording is relative to N4713.

  1. Modify [adjacent.difference] as indicated:

    template<class InputIterator, class OutputIterator>
      OutputIterator
        adjacent_difference(InputIterator first, InputIterator last, OutputIterator result);
    template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
      ForwardIterator2
        adjacent_difference(ExecutionPolicy&& exec,
                            ForwardIterator1 first, ForwardIterator1 last, ForwardIterator2 result);
    
    template<class InputIterator, class OutputIterator, class BinaryOperation>
      OutputIterator
        adjacent_difference(InputIterator first, InputIterator last,
                            OutputIterator result, BinaryOperation binary_op);
    template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
             class BinaryOperation>
      ForwardIterator2
        adjacent_difference(ExecutionPolicy&& exec,
                            ForwardIterator1 first, ForwardIterator1 last,
                            ForwardIterator2 result, BinaryOperation binary_op);
    

    -?- Let T be the value type of decltype(first). For the overloads that do not take an argument binary_op, let binary_op be an lvalue that denotes an object of type const minus<>.

    -1- Requires:

    1. (1.1) — For the overloads with no ExecutionPolicy, InputIterator’s value type T shall be MoveAssignable (Table 25) and shall be constructible from the type of *first. acc (defined below) shall be writable ([iterator.requirements.general]) to the result output iterator. The result of the expression val - std::move(acc) or binary_op(val, std::move(acc)) shall be writable to the result output iterator.

    2. (1.2) — For the overloads with an ExecutionPolicy, the value type of ForwardIterator1 shall be CopyConstructible (Table 24), constructible from the expression *first - *first or binary_op(*first, *first), and assignable to the value type of ForwardIterator2result of the expressions binary_op(*first, *first) and *first shall be writable to result.

    3. (1.3) — […]

    -2- Effects: For the overloads with no ExecutionPolicy and a non-empty range, the function creates an accumulator acc whose type is InputIterator’s value type of type T, initializes it with *first, and assigns the result to *result. For every iterator i in [first + 1, last) in order, creates an object val whose type is InputIterator’s value type T, initializes it with *i, computes val - std::move(acc) or binary_op(val, std::move(acc)), assigns the result to *(result + (i - first)), and move assigns from val to acc.

    -3- For the overloads with an ExecutionPolicy and a non-empty range, first the function creates an object whose type is ForwardIterator1's value type, initializes it with *first, and assigns the result to *result. Then for every d in [1, last - first - 1], creates an object val whose type is ForwardIterator1's value type, initializes it with *(first + d) - *(first + d - 1) or binary_op(*(first + d), *(first + d - 1)), and assigns the result to *(result + d)performs *result = *first. Then, for every d in [1, last - first - 1], performs *(result + d) = binary_op(*(first + d), *(first + (d - 1))).

Date: 2018-02-15.00:00:00

[ 2018-02-13, Priority set to 3 after mailing list discussion ]

Date: 2018-02-02.00:00:00

Parallel adjacent_difference is presently specified to "create a temporary object whose type is ForwardIterator1's value type". Serial adjacent_difference does that because it needs to work with input iterators, and needs to work when the destination range exactly overlaps the input range. The parallel version requires forward iterators and doesn't allow overlap, so it can avoid making these temporaries.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2018-06-12 01:05:16adminsetmessages: + msg9881
2018-06-12 01:05:16adminsetstatus: voting -> wp
2018-05-06 19:23:13adminsetstatus: ready -> voting
2018-03-19 00:12:24adminsetmessages: + msg9764
2018-03-19 00:12:24adminsetstatus: new -> ready
2018-02-13 20:33:52adminsetmessages: + msg9681
2018-02-07 18:54:36adminsetmessages: + msg9670
2018-02-02 00:00:00admincreate