[PATCH] D33997: Implement the non-execution policy versions of `reduce` and `transform_reduce` for C++17

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

Suppose you have:

  struct A {};
  struct B {};
  
  A operator+(A, B);
  
  std::vector v;
  std::reduce(v.begin(), v.end(), A{});

The implementation in this patch works fine for the above case - as would 
`accumulate`, which it is equivalent to. However, `reduce` requires that "All 
of `binary_op(init, *first)`, `binary_op(*first, init)`, `binary_op(init, 
init)`, and `binary_op(*first, *first)` shall be convertible to T.", as all 
those operations may be needed for the parallel execution-policy overload of 
`reduce` (the requirement applies to the non-execution-policy overload as well).

E.g. in the above example, these operations are also required by `reduce`, even 
though the non-parallel implementation does not need them:

  A operator+(B, A);
  A operator+(A, A);
  A operator+(B, B);

Should the non-parallel implementation of `reduce` static_assert or SFINAE away 
when these requirements are not met? I think it might be desirable to ensure 
that if this won't compile:

  std::reduce(std::par, v.begin(), v.end(), A{});

Then this will also not compile:

  std::reduce(v.begin(), v.end(), A{});

(And the spec seems to suggest this too).




Comment at: include/numeric:154
+return reduce(__first, __last, 
+   typename iterator_traits<_InputIterator>::value_type{}, 
_VSTD::plus<>());
+}

In the spec, this overload of `reduce` is described as equivalent to `return 
reduce(std::forward(exec), first, last, typename 
iterator_traits::value_type{});`.  The overload that it calls 
(the three argument version that omits a binary operation) just forwards to the 
four-argument reduce, adding the `plus<>()` argument.

Is there a reason you wanted to avoid the extra layer of function call 
indirection (it should be inlined and optimized away, right)? What you have 
seems perfectly fine, I'm just curious though.



Comment at: test/std/numerics/numeric.ops/reduce/reduce_iter_iter.pass.cpp:37
+unsigned sa = sizeof(ia) / sizeof(ia[0]);
+test(Iter(ia), Iter(ia), 0);
+test(Iter(ia), Iter(ia+1), 1);

 Just to confirm, this should be 0 because the "default" init value is 
`iterator_traits<_InputIterator>::value_type{}`, and `int{}` gives you a 
determinate result (as opposed to `int()` which would not), correct?


https://reviews.llvm.org/D33997



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D33997: Implement the non-execution policy versions of `reduce` and `transform_reduce` for C++17

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

I think the test `reduce_iter_iter_T.pass.cpp` can be improved a little bit.

Right now, it:

- Tests that the three argument overload (iterators + init) work correctly when 
the iterator value type is the same as the init type.
- Tests that the return type of the three argument overload is correct in cases 
where the iterator value type differs from the init type.

It does not, however, test whether the result is correct when the iterator 
value type differs from the init type.

I'd suggest:

  void
  test_different_init_type()
  {
  char ca[] = {CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX};
  unsigned sa = sizeof(ca) / sizeof(ca[0]);
  test(ca, ca, int{0}, int{0});
  test(ca, ca+1, int{0}, int{CHAR_MAX});
  test(ca, ca+2, int{0}, int{2*CHAR_MAX});
  test(ca, ca+sa, int{0}, int{4*CHAR_MAX});
  }


https://reviews.llvm.org/D33997



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D33997: Implement the non-execution policy versions of `reduce` and `transform_reduce` for C++17

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

For all the `transform_` variants, the spec allows the "inner" operation (the 
transformation)'s return type is not constrained. We should have a test for 
this.

For example, consider the binary-unary `transform_reduce` implementation:

  template 
  inline _LIBCPP_INLINE_VISIBILITY
  _Tp
  transform_reduce(_InputIterator __first, _InputIterator __last, 
   _Tp __init,  _BinaryOp __b, _UnaryOp __u)
  {
  for (; __first != __last; ++__first)
  __init = __b(__init, __u(*__first));
  return __init;
  }

The standard says the algorithm requires all of the following expressions be 
convertible to `_Tp`:

- `__b(__init, __init)`
- `__b(__init, __u(*__first))`
- `__b(__u(*__first), __init)`
- `__b(__u(*__first), __u(*__first))`

So, the following code should be allowed:

  struct A {};
  struct B {};
  struct C {};
  
  B unary_op(C);
  A binary_op(A, A);
  A binary_op(A, B);
  A binary_op(B, A);
  A binary_op(B, B); 
  
  std::vector v;
  std::tranform_reduce(v.begin(), v.end(), A{}, binary_op, unary_op);

Similar cases can be constructed for all the other `transform_` overloads.

I'll try to find time later to put together a concrete test for this.


https://reviews.llvm.org/D33997



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34007: Implement inclusive_scan and transform_inclusive_scan

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

Minor note: there's a mix of tabs and spaces in this diff.


https://reviews.llvm.org/D34007



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34007: Implement inclusive_scan and transform_inclusive_scan

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

So, `inclusive_scan` should be equivalent to `partial_sum` for the non-parallel 
version.


https://reviews.llvm.org/D34007



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34007: Implement inclusive_scan and transform_inclusive_scan

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

Here's `partial_sum`:

  template 
  inline _LIBCPP_INLINE_VISIBILITY
  _OutputIterator
  partial_sum(_InputIterator __first, _InputIterator __last, _OutputIterator 
__result,
  _BinaryOperation __binary_op)
  {
  if (__first != __last)
  {
  typename iterator_traits<_InputIterator>::value_type __t(*__first);
  *__result = __t;
  for (++__first, (void) ++__result; __first != __last; ++__first, 
(void) ++__result)
  {
  __t = __binary_op(__t, *__first);
  *__result = __t;
  }
  }
  return __result;
  }

And here's the `inclusive_scan` that should be equivalent to that `partial_sum`:

  template 
  inline _LIBCPP_INLINE_VISIBILITY
  _OutputIterator
  inclusive_scan(_InputIterator __first, _InputIterator __last, 
 _OutputIterator __result, _BinaryOp __b)
  {
  if (__first != __last) {
  typename iterator_traits<_InputIterator>::value_type __init = 
*__first++;
  return inclusive_scan(__first, __last, __result, __b, __init);
   }
  
  return __result;
  }

The `inclusive_scan` that it forwards to is:

  template 
  inline _LIBCPP_INLINE_VISIBILITY
  _OutputIterator 
  inclusive_scan(_InputIterator __first, _InputIterator __last, 
 _OutputIterator __result, _BinaryOp __b,  _Tp __init)
  {
  *__result++ = __init;
  for (; __first != __last; ++__first) {
  __init = __b(__init, *__first);
  *__result++ = __init;
   }
  
  return __result;
  }

Inlining it, we get:

  template 
  inline _LIBCPP_INLINE_VISIBILITY
  _OutputIterator
  inclusive_scan(_InputIterator __first, _InputIterator __last, 
 _OutputIterator __result, _BinaryOp __b)
  {
  if (__first != __last) {
  typename iterator_traits<_InputIterator>::value_type __init = 
*__first++;
  *__result++ = __init;
  
  for (; __first != __last; ++__first) {
  __init = __b(__init, *__first);
  *__result++ = __init;
  }
   }
  
  return __result;
  }

That looks equivalent to the `partial_sum` implementation above, so I think it 
is correct.


https://reviews.llvm.org/D34007



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D33997: Implement the non-execution policy versions of `reduce` and `transform_reduce` for C++17

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

I think we need a test case like this for all of the `transform_*`s

  struct A {};
  struct B {};
  struct C {};
  
  B unary_op(C);
  B unary_op(A) { assert(false); /* unary op applied to init value! */ }
  A binary_op(A, A);
  A binary_op(A, B);
  A binary_op(B, A);
  A binary_op(B, B); 
  
  std::vector v;
  std::tranform_reduce(v.begin(), v.end(), A{}, binary_op, unary_op);

The "inner" transform operation should **never** be applied to the `init` 
parameter.




Comment at: include/numeric:209
+{
+   return transform_reduce(__first1, __last1, __first2, __init, 
_VSTD::plus<>(), _VSTD::multiplies<>());
+}

rsmith wrote:
> Missing _VSTD::
In the patch I downloaded from here, the spacing before the return is tabs, not 
spaces.



Comment at: 
test/std/numerics/numeric.ops/transform.reduce/transform_reduce_iter_iter_init_bop_uop.pass.cpp:44
+{
+   constexpr const _Tp operator()(const _Tp& __x) const noexcept { return 
2 * __x; }
+};

In the patch I downloaded from here, there is a tab right before `constexpr`.


https://reviews.llvm.org/D33997



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34038: Implement the non-parallel versions of exclusive_scan and transform_exclusive_scan

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added inline comments.



Comment at: include/numeric:182
+{
+   _Tp __saved = __init;
+   for (; __first != __last; ++__first, (void) ++__result) {

If `__first == __last`, this initialization is unnecessary; I'd refactor this 
so that we check for `__first != __last` first.



Comment at: include/numeric:208
+{
+   _Tp __saved = __init;
+   for (; __first != __last; ++__first, (void) ++__result) {

If `__first == __last`, this initialization is unnecessary; I'd refactor this 
so that we check for `__first != __last` first.


https://reviews.llvm.org/D34038



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34038: Implement the non-parallel versions of exclusive_scan and transform_exclusive_scan

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

https://gist.github.com/brycelelbach/137f1e45b737d615134e228ec0c84f3a <- some 
crude tests I wrote based on slides/notes of mine.


https://reviews.llvm.org/D34038



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34038: Implement the non-parallel versions of exclusive_scan and transform_exclusive_scan

2017-06-08 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

This implementation works, but performs unnecessary operations.

https://gist.github.com/brycelelbach/907ac3b8a74603cc189897ab789a65a9

The "next" result is calculated in each iteration; this means one unnecessary 
application of the binary op (the "next" result on the final iteration).


https://reviews.llvm.org/D34038



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34038: Implement the non-parallel versions of exclusive_scan and transform_exclusive_scan

2017-06-09 Thread Bryce Adelstein Lelbach via Phabricator via cfe-commits
wash added a comment.

The init typedef test looks good to me.

This all looks good to go.


https://reviews.llvm.org/D34038



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits