Why does std::reduce need commutativity?
std::reduce
requires both associativity and commutativity. Associativity is clearly needed for a parallel algorithm, since you want to perform the calculation on separate chunks and then combine them.
As for commutativity: According to a reddit post by MSVC STL developer Billy O'Neal, this is required in order to allow vectorization to SIMD instructions:
Commutativity is also necessary to enable vectorization, since the code you want for reduce to come out as something like:
vecRegister = load_contiguous(first); while (a vector register sized chunk is left) { first += packSize; vecRegister = add_packed(load_contiguous(first), vecRegister); } // combine vecRegister's packed components
etc., which given ints and SSE registers and a * b * c * d * e * f * g * h gives something like (a * e) * (b * f) * (c * g) * (d * h).
Most other languages aren't doing explicit things to make vectorizing their reduction possible. And nothing says we can't add a noncommutative_reduce or something like that in the future if someone comes up with a compelling use case.
The behavior is actually non-deterministic if the operation between the operands is not commutative. "non-deterministic" is not the same as "undefined". Floating point math is not commutative, for example. This is one reason why a call to std::reduce
may not be deterministic, because the binary function is applied in an unspecified order.
Refer to this note in the standard:
Note: The difference between
reduce
andaccumulate
is that reduce appliesbinary_op
in an unspecified order, which yields a nondeterministic result for non-associative or non-commutative binary_op such as floating-point addition. —end note ]