LoginSignup
14
14

More than 5 years have passed since last update.

Java 8のStream APIの性能まとめ

Last updated at Posted at 2014-09-09

はじめに

Java 8のStream APIの性能について簡潔にまとめた資料がないので、自前で2種類のベンチマークを作成しました。

No. ベンチマークの内容 処理の粒度 Stream不使用 Streamシリアル Streamパラレル
1 1~nの総和($\sum_{i=1}^n i$)を求める ○(△) ×
2 レーベンシュタイン距離を求める × ×

No.1は、IntStream.range(0, n).sum()などを使った小粒度の処理で、
No.2は、大量の単語についてレーベンシュタイン距離を計算する大粒度の処理です。

それぞれのベンチマークについて、Streamを使わない処理、Streamを利用したシリアル処理、Streamを利用したパラレル処理の3種類で速度性能を採取しました。

Streamのシリアル処理の例
Collection.stream().中間操作()...終端操作();
Streamのパラレル処理の例
Collection.parallelStream().中間操作()...終端操作();

結果、速度性能が良かったものの順に「○→△→×」を上表に記載しました。

速度性能結果の考察

粒度が小さい処理の場合

中間操作~終端操作の一連の処理のコストが小さい場合は、JITによる最適化が効くまでの間は「Streamを使わない処理」が最も速度性能が優れています。

しかし、サーバなど長期運用ではJITによる最適化により「Streamを利用したのシリアル処理」の速度性能は「Streamを使わない処理」と同程度になります。

なお、「Streamを利用したパラレル処理」の場合、パラレル制御のオーバーヘッドが大半を占めるため、JITによって最適化されても速度性能が優れることは望めません。

粒度が大きい処理の場合

中間操作~終端操作の一連の処理のコストが大きい場合は、「Streamを使用したパラレル処理」を積極的に検討してよい。

ただし、念のためにシリアル処理とパラレル処理の性能比較はしておいた方がよい。

Streamを利用したパラレル処理を行わない方がよい処理

次のようなケースはパラレル化しない方がよい。

  1. ラムダ式から外部変数にアクセスするもの
  2. サイズが未定のもの(Stream#iterateで生成した場合など)
  3. 順番が必要なもの

for文の単純なIntStream.forEach()への置換

for文の使用
for(int i=0; i<100; i++) {
    小粒度の処理
}
Stream.forEach()の使用
IntStream
    .range(0, 100)
    .forEach(小粒度の処理)

後者のIntStream.forEach()にはメリットがなく、多用するとオブジェクトを多く生成するため、ガーベジコレクションが頻発しやすくなります。

また、可読性と性能の両方の観点からしても、前者のfor文の方がよいのではないでしょうか。

14
14
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
14