Why is filtering by primality in an inifinite stream of numbers taking forever if processed in parallel?

前端 未结 2 536
南方客
南方客 2021-02-14 03:14

I\'m creating an infinite stream of Integers starting at 200 Million, filter this stream using a naive primality test implementation to generate load and limit the result to 10.

相关标签:
2条回答
  • 2021-02-14 03:19

    Processing actually completes, though may take quite a long time depending on number of hardware threads on your machine. API documentation about limit warns that it might be slow for parallel streams.

    Actually the parallel stream first splits the computation to the several parts according to the available parallelism level, performs a computation for every part, then join the results together. How many parts do you have in your task? One per common FJP thread (=Runtime.getRuntime().availableProcessors()) plus (sometimes?) one for current thread if it's not in FJP. You can control it adding

    System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");
    

    Practically for your task the lower number you set, the faster it will compute.

    How to split the unlimited task? You particular task is handled by IteratorSpliterator which trySplit method creates chunks of ever-increasing size starting from 1024. You may try by yourself:

    Spliterator<Integer> spliterator = Stream.iterate(200_000_000, n -> ++n).spliterator();
    Spliterator[] spliterators = new Spliterator[10];
    for(int i=0; i<spliterators.length; i++) {
        spliterators[i] = spliterator.trySplit();
    }
    for(int i=0; i<spliterators.length; i++) {
        System.out.print((i+1)+": ");
        spliterators[i].tryAdvance(System.out::println);
    }       
    

    So the first chunk handles numbers of range 200000000-200001023, the second handles numbers of range 200001024-200003071 and so on. If you have only 1 hardware thread, your task will be split to two chunks, so 3072 will be checked. If you have 8 hardware threads, your task will be split to 9 chunks and 46080 numbers will be checked. Only after all the chunks are processed the parallel computation will stop. The heuristic of splitting the task to such a big chunks doesn't work good in your case, but you would see the performance boost had the prime numbers around that region appear once in several thousand numbers.

    Probably your particular scenario could be optimized internally (i.e. stop the computation if the first thread found that limit condition is already achieved). Feel free to report a bug to Java bug tracker.


    Update after digging more inside the Stream API I concluded that current behavior is a bug, raised an issue and posted a patch. It's likely that the patch will be accepted for JDK9 and probably even backported to JDK 8u branch. With my patch the parallel version still does not improve the performance, but at least its working time is comparable to sequential stream working time.

    0 讨论(0)
  • 2021-02-14 03:43

    The reason why parallel stream taking so long is due to the fact that all parallel streams uses common fork-join thread pool and since you are submitting a long running task(because your implementation of isPrime method is not efficient), you are blocking all threads in the pool and as a result of which all other tasks using parallel stream are blocked.

    In order to make the parallel version faster you can implement isPrime more efficiently. For e.g.

       Predicate<Integer> isPrime = new Predicate<Integer>() {
            @Override
            public boolean test(Integer n) {
                if(n < 2) return false;
                if(n == 2 || n == 3) return true;
                if(n%2 == 0 || n%3 == 0) return false;
                long sqrtN = (long)Math.sqrt(n)+1;
                for(long i = 6L; i <= sqrtN; i += 6) {
                    if(n%(i-1) == 0 || n%(i+1) == 0) return false;
                }
                return true;
            }
        };
    

    And immediately you will notice the improvement in the performance. In general avoid using parallel stream when there exists the possibility of blocking threads in the pool

    0 讨论(0)
提交回复
热议问题