使并行IntStream更有效/更快?

时间:2018-02-12 19:29:44

标签: java parallel-processing java-stream primes

我已经找了一会儿这个答案,但找不到任何东西。

我试图创建一个可以非常快速地找到素数的IntStream(很多很多素数,非常快 - 几秒钟就会有数百万个)。

我目前正在使用此parallelStream:

onMapReady

但生成数字需要很长时间。 (7546ms产生1,000,000个素数)。

有没有明显的方法可以提高效率/速度?

2 个答案:

答案 0 :(得分:3)

使用代码进行高效的并行处理有两个常见问题。首先,使用iterate,这不可避免地要求前一个元素计算下一个元素,这不是并行处理的良好起点。其次,您使用的是无限流。高效的工作负载拆分至少需要估计要处理的元素数量。

由于您正在处理递增的整数,因此在到达Integer.MAX_VALUE时存在明显的限制,但是流实现并不知道您实际上正在处理递增的数字,因此,会将您的正式无限流视为真正的无限的。

解决这些问题的解决方案是

public static IntStream stream() {
    return IntStream.rangeClosed(1, Integer.MAX_VALUE/2).parallel()
            .map(i -> i*2+1)
            .filter(i -> i % 3 != 0).mapToObj(BigInteger::valueOf)
            .filter(i -> i.isProbablePrime(1))
            .mapToInt(BigInteger::intValue);
}

但必须强调的是,在这种形式下,此解决方案仅在您真正想要处理整数范围内的全部或大部分素数时才有用。一旦将skiplimit应用于流,并行性能将显着下降,如这些方法的文档所指定的那样。此外,将filter与仅接受较小数值范围的值的谓词一起使用,意味着将会有许多不必要的工作,而不是并行完成。

您可以调整方法以接收值范围作为参数,以调整源IntStream的范围来解决此问题。

现在是时候强调算法对并行处理的重要性。考虑Sieve of Eratosthenes。以下实现

public static IntStream primes(int max) {
    BitSet prime = new BitSet(max>>1);
    prime.set(1, max>>1);
    for(int i = 3; i<max; i += 2)
        if(prime.get((i-1)>>1))
            for(int b = i*3; b>0 && b<max; b += i*2) prime.clear((b-1)>>1);
    return IntStream.concat(IntStream.of(2), prime.stream().map(i -> i+i+1));
}
尽管没有使用并行处理,但与其他方法相比,

的速度提高了一个数量级,即使使用Integer.MAX_VALUE作为上限(使用.reduce((a,b) -> b)的终端操作而不是{{ {1}}或toArray,以确保完整处理所有值,而不会增加额外的存储空间或打印成本。)

如果你有一个特定的候选人或想要处理一小部分数字(或当数字超出forEach(System.out::println)或甚至isProbablePrime时,int很有用。范围)¹,但是为了处理大数字的升序序列,有更好的方法,并行处理不是性能问题的最终答案。

¹考虑,例如

long

答案 1 :(得分:1)

通过做一些修改,似乎我可以做得比你现有的好1/2:

return IntStream.iterate(3, i -> i + 2)
            .parallel()
            .unordered()
            .filter(i -> i % 3 != 0)
            .mapToObj(BigInteger::valueOf)
            .filter(i -> i.isProbablePrime(1))
            .mapToInt(BigInteger::intValue);
相关问题