gpt4 book ai didi

java - 为什么在某些情况下 Java Stream.forEach 方法比其他循环更快?

转载 作者:行者123 更新时间:2023-12-03 19:25:43 26 4
gpt4 key购买 nike

我目前正在开展一个项目,在该项目中我使用 Java Microbenchmark Harness (JMH) 框架测量 Java 中不同类型循环的速度。我得到了一些关于流的有趣结果,我无法解释,并且想知道是否更好地理解流和数组列表的人可以帮助我解释我的结果。

基本上,当遍历大小为 100 的数组列表时,stream.forEach 方法比任何其他类型的循环快得多:

我的结果图如下所示:
https://i.imgur.com/ypXoWWq.png

我试过使用对象和字符串的数组列表,都产生了类似的结果。随着列表的大小变大,stream 方法仍然更快,但其他列表之间的性能差距越来越小。我不知道是什么导致了这些结果。

@Fork(5)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class StackOverflowQ {

List<Integer> intList;

int size = 100;

@Setup
public void setup() {
intList = new ArrayList<>(size);
for(int i = 0; i<size;i++){
intList.add(i);
}
}

/**
Work done to each item in the loop.
*/
public double doWork(int item) {
return item + 47;
}

@Benchmark
public void standardFor(Blackhole bh){
for (int i = 0; i<intList.size(); i++){
bh.consume(doWork(intList.get(i)));
}
}

@Benchmark
public void streamForEach(Blackhole bh){
intList.stream().forEach(i -> bh.consume(doWork(i)));
}

}

最佳答案

这个答案是关于 java.util.ArrayList .其他 Collection实现可能(并且确实!)显示完全不同的结果。
简而言之:因为流使用 Spliterator而不是 Iterator解释:Iterator基于迭代基于 hasNextnext方法调用,其中第二个改变了 Iterator实例。这会带来一些性能成本。 Spliterator支持批量迭代。 Read more about this here.增强的 for 循环( for (T e : iterable) { ... } )似乎使用了 Iterator .
性能通常应该是次要的问题;你应该使用最能描述你的意图的结构。虽然由于向后兼容的原因它会很困难,但基于 forEach 的拆分器和增强的 for 循环(在 ArrayList 上)之间的性能差异可能会在 future 消失。
索引 for 循环怎么样? ( List#get(int) )
它们表现出比拆分器差的部分性能是因为它们需要验证索引。其他原因可能包括方法调用,例如。获取索引处的数据,而 Spliterator直接访问数组。但这纯粹是猜测。
微小的 JMH 基准
您可以在下面看到一个证实上述理论的微小基准。请注意,最佳情况下,基准测试应该运行更长时间。

@State(Scope.Benchmark)
@Fork(value = 2)
@Warmup(iterations = 2, time = 3)
@Measurement(iterations = 2, time = 3)
public class A {

public List<Object> list;

@Setup
public void setup() {
list = new ArrayList<>();
for (int i = 0; i < 1000; i++) list.add(i);
}

@Benchmark
public void collectionForEach(Blackhole hole) {
list.forEach(hole::consume);
}

@Benchmark
public void iteratorFor(Blackhole hole) {
for (Iterator<Object> iterator = list.iterator(); iterator.hasNext(); ) {
hole.consume(iterator.next());
}
}

@Benchmark
public void enhancedFor(Blackhole hole) {
for (Object e : list) {
hole.consume(e);
}
}

@Benchmark
public void iteratorForEach(Blackhole hole) {
list.iterator().forEachRemaining(hole::consume);
}

@Benchmark
public void indexedFor(Blackhole hole) {
for (int i = 0, size = list.size(); i < size; i++) {
hole.consume(list.get(i));
}
}

@Benchmark
public void streamForEach(Blackhole hole) {
list.stream().forEach(hole::consume);
}

@Benchmark
public void spliteratorForEach(Blackhole hole) {
list.spliterator().forEachRemaining(hole::consume);
}
}
和结果。 “ops/s”表示操作/秒,越高越好。
Benchmark              Mode  Cnt       Score      Error  Units
A.collectionForEach thrpt 4 158047.064 ± 959.534 ops/s
A.iteratorFor thrpt 4 177338.462 ± 3245.129 ops/s
A.enhancedFor thrpt 4 177508.037 ± 1629.494 ops/s
A.iteratorForEach thrpt 4 184919.605 ± 1922.114 ops/s
A.indexedFor thrpt 4 193318.529 ± 2715.611 ops/s
A.streamForEach thrpt 4 280963.272 ± 2253.621 ops/s
A.spliteratorForEach thrpt 4 283264.539 ± 3055.967 ops/s

关于java - 为什么在某些情况下 Java Stream.forEach 方法比其他循环更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57423942/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com