为了测试Java 8的流和自动并行化的新实现,我运行了以下简单测试:
ArrayList<Integer> nums = new ArrayList<>();
for (int i=1; i<49999999; i++) nums.add(i);
int sum=0;
double begin, end;
begin = System.nanoTime();
for (Integer i : nums) sum += i;
end = System.nanoTime();
System.out.println( "1 core: " + (end-begin) );
begin = System.nanoTime();
sum = nums.parallelStream().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println( "8 cores: " + (end-begin) );
我认为总结一系列整数将能够充分利用所有8个核心,但输出看起来像这样:
1 core: 1.70552398E8
8 cores: 9.938507635E9
我知道nanoTime()在多核系统中存在问题,但我怀疑这是问题,因为我关闭了一个数量级。
我的操作是否如此简单,以至于reduce()所需的开销是否克服了多核的优势?
答案 0 :(得分:2)
您的流示例有2个取消装箱(Integer.sum(int,int)
)和一个装箱(结果int
必须转换回Integer
)每个号码,而for循环只有一个拆箱。所以两者没有可比性。
当您计划使用整数进行计算时,最好使用IntStream
:
nums.stream().mapToInt(i -> i).sum();
这会给你一个类似于for循环的性能。我的机器上的并行流仍然较慢。
最快的选择是这个顺便说一句:
IntStream.rangeClosed(0, 49999999).sum();
更快的顺序,没有首先构建列表的开销。当然,它只是这个特殊用例的替代品。但它表明重新思考现有方法而不仅仅是“添加流”是值得的。
答案 1 :(得分:1)
要在所有中正确比较,您需要为这两个操作使用类似的开销。
ArrayList<Integer> nums = new ArrayList<>();
for (int i = 1; i < 49999999; i++)
nums.add(i);
int sum = 0;
long begin, end;
begin = System.nanoTime();
sum = nums.stream().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("1 core: " + (end - begin));
begin = System.nanoTime();
sum = nums.parallelStream().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("8 cores: " + (end - begin));
这让我感动
1 core: 769026020
8 cores: 538805164
parallelStream()
实际上更快。 (注意:我只有4个核心,但parallelSteam()
并不总是使用所有核心)
另一件事是拳击和拆箱。有nums.add(i)
的装箱,以及进入Integer::sum
的所有内容的拆箱,需要两个int
秒。我将此测试转换为数组以删除它:
int[] nums = new int[49999999];
System.err.println("adding numbers");
for (int i = 1; i < 49999999; i++)
nums[i - 1] = i;
int sum = 0;
System.err.println("begin");
long begin, end;
begin = System.nanoTime();
sum = Arrays.stream(nums).reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("1 core: " + (end - begin));
begin = System.nanoTime();
sum = Arrays.stream(nums).parallel().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("8 cores: " + (end - begin));
这给出了意想不到的时间:
1 core: 68050642
8 cores: 154591290
对于具有常规整数的线性减少,速度快得多(1-2个数量级),但是并行减少仅约1/4的时间并且最终变慢。 我不确定为什么会这样,但它确实很有趣!
进行了一些分析,结果是fork()
执行并行流的方法非常昂贵,因为使用了ThreadLocalRandom
,它为网络接口调用它的种子!这非常慢,这是parallelStream()
慢于stream()
的唯一原因!
我的一些VisualVM数据:(忽略await()
时间,这是我使用的方法,因此我可以跟踪程序)
对于第一个示例:https://www.dropbox.com/s/z7qf2es0lxs6fvu/streams1.nps?dl=0
第二个例子:https://www.dropbox.com/s/f3ydl4basv7mln5/streams2.nps?dl=0
TL; DR:在您的Integer
情况下,它看起来像是并行获胜,但是int
情况会导致并行速度变慢。