I want to calculate a^b , e.g. 2^30,
public long pow(final int a, final int b)
first I used this manner
return LongStream.range(0, b).reduce(1, (acc, x) -> a * acc); // 1073741824
Got right result. Then I want to calculate it parallelly, so naturally I changed it to
return LongStream.range(0, b).parallel().reduce(1, (acc, x) -> a * acc); // 32
but in this case the result is just 32
. Why?
So for supporting parallel I changed it again
return Collections.nCopies(b,a).parallelStream().reduce(1, (acc, x) -> acc * x); // 1073741824
in this case it works.
So what's wrong with parallel
manner?
After tracing source code, I finally knew why the result is 32.
Related source code
because actually not used
x
in lambdaso the actual effect is like this
Online demo to simulate this phenomenon.
reduce requires that the supplied function be associative. Your function
(acc, x) -> a * acc
does not satisfy the requirement and thus violates the contract.To be associative, the function must satisfy
(x op y) op z == x op (y op z)
for any x, y and z. But for your function,(x op y) op z = x*a^2
whilex op (y op z) = x * a
.Furthermore, the first parameter supplied to reduce must be an identity with respect to the accumulator function. So it must be true that
1 op x == x
for any x. But that also doesn't hold for your accumulator function since1 op x == a
.The correct way to do this is:
This is guaranteed to work correctly whether the stream is parallel or sequential.