我正在使用Stream并行处理,并了解如果我使用平面阵列流,它会得到非常快速的处理。但是如果我使用ArrayList
,则处理速度会慢一些。但是如果我使用LinkedList
或一些二进制树,处理速度会慢得多。
所有听起来更像是流的可分割性,处理速度越快。这意味着阵列和数组列表在并行流的情况下最有效。这是真的吗?如果是这样,如果我们想并行处理流,我们总是使用ArrayList
或数组吗?如果是这样,如果在并行流的情况下如何使用LinkedList
和BlockingQueue
?
另一件事是选择的中间函数的状态。如果我执行filter()
,map()
等无状态操作,则性能很高,但如果执行状态完整操作,例如distinct()
,sorted()
,limit()
,{ {1}},需要花费很多时间。再次,并行流变慢。这是否意味着我们不应该在并行流中使用状态全中间函数?如果是这样,那么解决这个问题的方法是什么?
答案 0 :(得分:5)
好吧,正如this question中所讨论的那样,几乎没有任何理由可以使用LinkedList
。较高的迭代成本适用于所有操作,而不仅仅是并行流。
通常,拆分支持确实对并行性能有很大影响。首先,它是否具有真正的,有希望的廉价分裂支持而不是继承AbstractSpliterator
的缓冲默认行为,第二,分裂的平衡程度如何。
在这方面,没有理由认为二叉树应该表现不佳。树可以很容易地分成子树,如果树在开始时是平衡的,那么分裂也会平衡。当然,这要求实际的Collection
实现实现spliterator()
方法,返回合适的Spliterator
实现,而不是继承default
方法。例如。 TreeSet
有专门的分裂者。尽管如此,迭代子树可能比迭代一个数组更昂贵,但这不是并行处理的属性,因为它也适用于顺序处理或者通常对元素进行任何类型的迭代。
如何在并行流的情况下使用LinkedList
和BlockingQueue
这个问题没有实际意义。您可以根据应用程序的需要选择集合类型,如果您确实需要其中一种(在LinkedList
很难想象的情况下),那么您可以使用它,并使用它的并行流性能小于ArrayList
的那个,显然不符合你的其他需要。没有一般技巧可以更好地实现严重可拆分集合的并行流性能。如果有,它将成为图书馆的一部分。
在某些极端情况下,JRE无法提供最高性能,这将在Java 9中得到解决,例如String.chars()
,Files.lines()
或第3部分{{1}的默认分裂器} RandomAccess
s,但这些都不适用于List
,LinkedList
或自定义二叉树实现。
换句话说,如果您对特定集合有特定用例,则可能需要改进,但没有任何技巧可以提高所有集合的所有任务的并行性能。
正确的中间操作(如BlockingQueue
,distinct()
,sorted()
,limit()
对并行流的成本较高是正确的,他们的文档甚至可以说明这一点。所以我们可以给出一般建议来避免它们,特别是对于并行流,但是如果你不需要它们,那就没有用了,因为你没有使用它们。同样,没有一般的解决办法,因为如果有一个更好的替代方案,提供这些操作没有多大意义。
答案 1 :(得分:4)
IMO也不错。
当然,array
和ArrayList
的分割效果要比LinkedList
或某种类型的Tree
好得多。你可以看看他们Spliterators
是如何让自己说服的。它们通常从一些batch size
(1024个元素)开始,并从中增加。如果我没记错的话,LinkedList
可以做到Files.lines
。所以,是的,使用数组和ArrayList
将具有非常好的并行化。
如果你想为LinkedList
这样的结构提供更好的并行支持,你可以编写自己的分裂器 - 我认为StreamEx
为Files.lines
做了一个较小的批量大小。 。this是一个相关的问题。
另一件事是,当你使用有状态的中间操作时 - 你将有效地使那些有状态的above
的中间操作变成有状态的......让我举一个例子:
IntStream.of(1, 3, 5, 2, 6)
.filter(x -> {
System.out.println("Filtering : " + x);
return x > 2;
})
.sorted()
.peek(x -> System.out.println("Peek : " + x))
.boxed()
.collect(Collectors.toList());
这将打印:
Filtering : 1
Filtering : 3
Filtering : 5
Filtering : 2
Filtering : 6
Peek : 3
Peek : 5
Peek : 6
由于您已使用sorted
且filter
高于此值,因此过滤器必须获取所有元素并对其进行处理 - 以便将sorted
应用于正确的元素。
另一方面,如果您放弃sorted
:
IntStream.of(1, 3, 5, 2, 6)
.filter(x -> {
System.out.println("Filtering : " + x);
return x > 2;
})
// .sorted()
.peek(x -> System.out.println("Peek : " + x))
.boxed()
.collect(Collectors.toList());
输出将是:
Filtering : 1
Filtering : 3
Peek : 3
Filtering : 5
Peek : 5
Filtering : 2
Filtering : 6
Peek : 6
一般来说,我同意,我尽量避免(如果可以的话)有状态的中间操作 - 可能你不想sorted
让我们说 - 可能你可以收集到{ {1}} ...等等但是我不要过度思考它 - 它我需要使用它 - 我只是这样做而且可能衡量它是否真的是一个瓶颈。
除非你真的遇到一些性能问题 - 我不会考虑那么多;特别是因为你需要 lot 元素才能从并行中获得一些速度优势。
Here是一个相关问题,表明您确实需要大量元素才能看到效果增益。