※2017.1.23 もろもろ勘違いして記載していたので大幅書き換え
Forループって複数コア使ってくれるの? Stream/ParallelStreamは?
とか気になったの簡単に比べてみた。
StreamとParallelStreamの挙動についてはJavaOne2013のJava 8 Streams: Lambda in Top Gearのスライドを見るのが良さげ。
一応どの計測も2,3回実行した後に行っています。
Forループ
public class StreamTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 1; i <= 200000; i++) {
list.add(String.valueOf(i));
}
long start = System.currentTimeMillis();
String str = "";
for (String val : list) {
str += val;
}
System.out.println(str);
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
所要時間は42801ms
一応4コアとも使っているっぽい。けどコアあたりのCPU使用率は100%にはならず。
Stream
public class StreamTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 1; i <= 200000; i++) {
list.add(String.valueOf(i));
}
long start = System.currentTimeMillis();
Optional<String> str = list.stream().reduce((val1, val2) -> val1 + val2);
System.out.println(str);
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
所要時間は41039ms
Forループとほぼ同じ。全体に負荷が少ないかなー、という感じ。
ParallelStream
public class StreamTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 1; i <= 200000; i++) {
list.add(String.valueOf(i));
}
long start = System.currentTimeMillis();
Optional<String> str = list.parallelStream().reduce((val1, val2) -> val1 + val2);
System.out.println(str);
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
所要時間は1937ms
一瞬全コアが100%になってすぐに終わる。20倍くらい早い。ちなみに要素数を減らすと差は小さくなっていくので、要素数が多ければ多いほど差が出ると思われる。
まとめ
(使える場所では 処理する要素数が多くなる場所では)ParallelStreamを積極的に検討していくべき。
For/Streamに関しては複数コアに処理を振り分けてはいるものの、順序を保証するための同期処理でCPU待ちが発生しているように見える。なのでコアあたりの使用率が100%にならず、処理時間も長くかかる。
その分、ParallelStreamでは同期による待ちがない分、CPUが常に稼働している状態になり使用率が100%まで上がっている様子。
Forが複数コア利用しているのが予想外だったけど、それ以外は思っていた通りの挙動だったので色々納得。
ちなみに今回のテストだとParallelStreamでも処理順序が乱れていなかったんだけれど、おそらくループ内の処理時間が一定だったからたまたま順序が維持されたのだと思う。
逆にFor/Streamでも同期チェック時はほぼ必ず次の処理に行ける判定だった(待ち判定はなかった)と思うが、それでもこれだけの速度差が出ているということは、順序保証のオーバーヘッドはだいぶ大きいのかな。
処理順序というか、出力結果の順はStreamとParallelStreamの違いには直接関係なかった。で、速度差については純粋な計算量でした。
ParallelStreamについて完全に勘違いしていたよ……Java 8 Streams: Lambda in Top Gearの45:15あたりを参照。コメント指摘感謝。