On my machine, the program below prints:
OptionalLong[134043]
PARALLEL took 127869 ms
OptionalLong[134043]
SERIAL took 60594 ms
It's not clear to my why executing the program in serial is faster than executing it in parallel. I've given both programs -Xms2g -Xmx2g
on an 8gb
box thats relatively quiet. Can someone clarify whats going on?
import java.util.stream.LongStream;
import java.util.stream.LongStream.Builder;
public class Problem47 {
public static void main(String[] args) {
final long startTime = System.currentTimeMillis();
System.out.println(LongStream.iterate(1, n -> n + 1).parallel().limit(1000000).filter(n -> fourConsecutives(n)).findFirst());
final long endTime = System.currentTimeMillis();
System.out.println(" PARALLEL took " +(endTime - startTime) + " ms");
final long startTime2 = System.currentTimeMillis();
System.out.println(LongStream.iterate(1, n -> n + 1).limit(1000000).filter(n -> fourConsecutives(n)).findFirst());
final long endTime2 = System.currentTimeMillis();
System.out.println(" SERIAL took " +(endTime2 - startTime2) + " ms");
}
static boolean fourConsecutives(final long n) {
return distinctPrimeFactors(n).count() == 4 &&
distinctPrimeFactors(n + 1).count() == 4 &&
distinctPrimeFactors(n + 2).count() == 4 &&
distinctPrimeFactors(n + 3).count() == 4;
}
static LongStream distinctPrimeFactors(long number) {
final Builder builder = LongStream.builder();
final long limit = number / 2;
long n = number;
for (long i = 2; i <= limit; i++) {
while (n % i == 0) {
builder.accept(i);
n /= i;
}
}
return builder.build().distinct();
}
}
We can make it easier to execute in parallel, but we can't necessarily make parallelism easy.
The culprit in your code is the combination of limit+parallel. Implementing limit() is trivial for sequential streams, but fairly expensive for parallel streams. This is because the definition of the limit operation is tied to the encounter order of the stream. Streams with limit() are often slower in parallel than in sequential, unless the computation done per element is very high.
Your choice of stream source is also limiting parallelism. Using iterate(0, n->n+1)
gives you the positive integers, but iterate
is fundamentally sequential; you can't compute the nth element until you've computed the (n-1)th element. So when we try and split this stream, we end up splitting (first, rest). Try using range(0,k)
instead; this splits much more nicely, splitting neatly by halves with random access.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With