|
15 | 15 | 1. GC的影响。GC的行为是Java中很不好控制的一块,为增加确定性,我们手动指定使用CMS收集器,并使用10GB固定大小的堆内存。集体到JVM参数就是`-XX:+UseConcMarkSweepGC -Xms10G -Xmx10G`
|
16 | 16 | 2. JIT(Just-In-Time)即时编译技术。即时编译技术会将热点代码在JVM运行的过程中编译成本地代码,测试时我们会先对程序预热,触发对测试函数的即时编译。相关的JVM参数是`-XX:CompileThreshold=10000`。
|
17 | 17 |
|
| 18 | +Stream并行执行时用到`ForkJoinPool.commonPool()`得到的线程池,为控制并行度我们使用Linux的`taskset`命令指定JVM可用的核数。 |
| 19 | + |
18 | 20 | 测试数据由程序随机生成。为防止一次测试带来的抖动,测试4次求出平均时间作为运行时间。
|
19 | 21 |
|
20 | 22 |
|
|
25 | 27 |
|
26 | 28 | 测试程序[IntTest](./perf/StreamBenchmark/src/lee/IntTest.java),测试结果如下图:
|
27 | 29 |
|
28 |
| -<img src="./Figures/perf_Stream_min_int.png" width="500px" align="center" alt="perf_Stream_min_int"/> |
| 30 | +<img src="./Figures/perf_Stream_min_int.png" width="600px" align="center" alt="perf_Stream_min_int"/> |
29 | 31 |
|
30 | 32 | 图中展示的是for循环外部迭代耗时为基准的时间比值。分析如下:
|
31 | 33 |
|
32 | 34 | 1. 对于基本类型Stream串行迭代的性能开销明显高于外部迭代开销(两倍);
|
33 | 35 | 2. Stream并行迭代的性能比串行迭代和外部迭代都好。
|
34 | 36 |
|
35 |
| -并行迭代性能跟跟可利用的核数有关,所有我们专门测试了不同核数下的Stream并行迭代效果: |
| 37 | +并行迭代性能跟可利用的核数有关,所有我们专门测试了不同核数下的Stream并行迭代效果: |
36 | 38 |
|
37 | 39 | <img src="./Figures/perf_Stream_min_int_par.png" width="500px" align="center" alt="perf_Stream_min_int_par"/>
|
38 | 40 |
|
|
52 | 54 |
|
53 | 55 | 测试程序[StringTest](./perf/StreamBenchmark/src/lee/StringTest.java),测试结果如下图:
|
54 | 56 |
|
55 |
| -<img src="./Figures/perf_Stream_min_String.png" width="500px" align="center" alt="perf_Stream_min_String"/> |
| 57 | +<img src="./Figures/perf_Stream_min_String.png" width="600px" align="center" alt="perf_Stream_min_String"/> |
56 | 58 |
|
57 | 59 | 结果分析如下:
|
58 | 60 |
|
|
76 | 78 |
|
77 | 79 | 测试内容:给定订单列表,统计每个用户的总交易额。对比使用外部迭代手动实现和Stream API之间的性能。
|
78 | 80 |
|
79 |
| -我们将订单简化为`<userName, price, timeStamp>`构成的元组,并用`Order`对象来表示。测试程序[ReductionTest](./perf/StreamBenchmark/src/lee/ReductionTest.java),测试结果如下图: |
| 81 | +我们将订单简化为`<userName, price, timeStamp>`构成的元组,并用`Order`对象来表示。测试程序[ReductionTest](./perf/StreamBenchmark/src/lee/ReductionTest.java),测试结果如下图: |
80 | 82 |
|
81 |
| -<img src="./Figures/perf_Stream_reduction.png" width="500px" align="center" alt="perf_Stream_reduction"/> |
| 83 | +<img src="./Figures/perf_Stream_reduction.png" width="600px" align="center" alt="perf_Stream_reduction"/> |
82 | 84 |
|
83 | 85 | 分析,对于复杂的归约操作:
|
84 | 86 |
|
|
103 | 105 | 1. 对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
|
104 | 106 | 2. 对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。
|
105 | 107 |
|
106 |
| -所以,如果出于性能(而不是代码的简洁)考虑,1. 对于简单操作推荐通过外部迭代手动实现,2. 对于复杂操作,推荐使用Stream API, 3. 在多核情况下,推荐使用并行Stream API来发挥多核优势。 |
| 108 | +所以,如果出于性能(而不是代码的简洁)考虑,1. 对于简单操作推荐通过外部迭代手动实现,2. 对于复杂操作,推荐使用Stream API, 3. 在多核情况下,推荐使用并行Stream API来发挥多核优势,4.单核情况下不建议使用并行Stream API。 |
107 | 109 |
|
108 | 110 | 即使是从性能方面说,尽可能的使用Stream API也另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。
|
0 commit comments