java - 在多线程的情况下使用流限制的最佳性能方式

标签 java multithreading random java-8 java-stream

我在 InfoQ 上看了 José Paumard 的演讲:http://www.infoq.com/fr/presentations/jdk8-lambdas-streams-collectors (法语)

问题是我卡在了这一点上。 要使用流 AND 多线程收集 1M Long,我们可以这样做:

Stream<Long> stream = 
  Stream.generate(() -> ThreadLocalRandom.current().nextLong()) ;

List<Long> list1 = 
  stream.parallel().limit(10_000_000).collect(Collectors.toList()) ;

但考虑到线程总是检查上述限制会影响性能。

在那次谈话中,我们还看到了第二种解决方案:

Stream<Long> stream = 
  ThreadLocalRandom.current().longs(10_000_000).mapToObj(Long::new) ;

List<Long> list = 
  stream.parallel().collect(Collectors.toList()) ;

而且它似乎在性能方面更好。

所以这是我的问题:为什么第二个代码更好,是否有更好或至少成本更低的方法来做到这一点?

最佳答案

这是一个依赖于实现的限制。关注并行性能的开发人员必须了解的一件事是,可预测的流大小通常有助于并行性能,因为它们允许平衡地分配工作负载。

这里的问题是,通过 Stream.generate()limit() 创建的无限流的组合不会产生大小可预测的流,尽管它对我们来说看起来完全可以预测。

我们可以使用以下辅助方法检查它:

static void sizeOf(String op, IntStream stream) {
    final Spliterator.OfInt s = stream.spliterator();
    System.out.printf("%-18s%5d, %d%n", op, s.getExactSizeIfKnown(), s.estimateSize());
}

然后

sizeOf("randoms with size", ThreadLocalRandom.current().ints(1000));
sizeOf("randoms with limit", ThreadLocalRandom.current().ints().limit(1000));
sizeOf("range", IntStream.range(0, 100));
sizeOf("range map", IntStream.range(0, 100).map(i->i));
sizeOf("range filter", IntStream.range(0, 100).filter(i->true));
sizeOf("range limit", IntStream.range(0, 100).limit(10));
sizeOf("generate limit", IntStream.generate(()->42).limit(10));

将打印

randoms with size  1000, 1000
randoms with limit   -1, 9223372036854775807
range               100, 100
range map           100, 100
range filter         -1, 100
range limit          -1, 100
generate limit       -1, 9223372036854775807

所以我们看到,某些源,如 Random.ints(size)IntStream.range(…) 产生具有可预测大小的流和某些中间操作,如 map 能够携带信息,因为他们知道大小不受影响。其他如 filterlimit 不会传播大小(作为已知的精确大小)。

很明显,filter 无法预测元素的实际数量,但它提供源大小作为估计值,这是合理的,因为这是可以通过过滤器的最大元素数。

相比之下,当前的 limit 实现不提供大小,即使源具有精确大小并且我们知道可预测的大小与 min(source size, limit )。相反,它甚至报告了一个无意义的估计大小(源的大小),尽管已知结果大小永远不会高于限制。在无限流的情况下,我们有额外的障碍 Spliterator流所基于的接口(interface)没有办法报告它是无限的。在这些情况下,无限流 + 限制返回 Long.MAX_VALUE 作为估计值,这意味着“我什至无法猜测”。

因此,根据经验,在当前的实现中,如果可以在流源中预先指定所需大小,程序员应避免使用 limit。但是由于 limitordered 并行流(不适用于 randoms 或 generate)的情况下也有明显的(已记录的)缺点,大多数开发人员无论如何都会避免 limit

关于java - 在多线程的情况下使用流限制的最佳性能方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33929363/

相关文章:

javascript - 如何在服务器上运行浏览器功能?

java - 这段java继承代码有什么问题?

python - 线程内的 print() 输出错误的值

Python 3 - 在用户将鼠标悬停在 GUI 窗口上之前,主线程未检测到后台线程中的键盘中断

java - 通过 JDBC Access Access(使用 ODBC?)

java - 同步(this)和同步(其他对象)之间有什么区别

python - Numpy:如何将矩阵随机拆分/选择为 n 个不同的矩阵

c++ - 尽管不是 const,所有 <random> 发行版都是线程安全的吗?

c# - 如何从 C# 中的列表或 var 中获取随机数量的详细信息

java - 广播接收器通知链接到我的应用程序