web-dev-qa-db-ja.com

.stream()。parallel()が同じことをするときにCollection.parallelStream()が存在するのはなぜですか?

Java 8では、Collectionインターフェースが拡張され、_Stream<E>_を返す2つのメソッドが追加されました:stream()(シーケンシャルストリームを返します)とparallelStream()、おそらく並列ストリームを返します。ストリーム自体にも、同等の並列ストリームを返すparallel()メソッドがあります(現在のストリームを並列に変更するか、新しいストリームを作成します)。

重複には明らかな欠点があります。

  • ややこしい。質問は ストリームが並列であることを確認するために、両方のparallelStream()。parallel()を呼び出す必要があるかどうか を尋ねます。保証できない場合、なぜparallelStream()が存在するのですか?逆もまた混乱しています-parallelStream()がシーケンシャルストリームを返す場合、おそらく理由があります(たとえば、並列ストリームがパフォーマンストラップである本質的にシーケンシャルなデータ構造)。そのようなストリームに対してStream.parallel()は何をすべきですか? (UnsupportedOperationExceptionは、parallel()の仕様では許可されていません。)

  • インターフェースにメソッドを追加すると、既存の実装に、互換性のない戻りの型を持つ同様の名前のメソッドがある場合に、競合が発生するリスクがあります。 stream()に加えてparallelStream()を追加すると、ほとんど利益が得られないリスクが2倍になります。 (名前の衝突を回避するために、または別の理由で名前が変更されたかどうかはわかりませんが、ある時点でparallelStream()は、単にparallel()という名前でした。

Collection.stream()。parallel()を呼び出すときにCollection.parallelStream()が存在するのはなぜですか?

50
Jeffrey Bosboom

Collection.(parallelS|s)tream()StreamのJavadoc自体は質問に答えないため、根拠のメーリングリストに参加します。私はlambda-libs-spec-observersアーカイブを調べて、 (Collection.parallelStream()について特に1つのスレッドJava.util.ArraysがparallelStream( ) 一致させる(または実際には削除する必要があるかどうか)。一度きりの結論はなかったので、別のリストから何かを逃したか、その問題は私的な議論で解決されたのでしょう。 (おそらく Brian Goetz 、このディスカッションのプリンシパルの1つで、足りないものはすべて記入できます。)

参加者はうまくポイントを述べたので、この回答はほとんどの場合、[括弧]にいくつかの明確化を加えた、関連する引用の構成にすぎません。重要です(私が解釈すると)。

parallelStream()は非常に一般的なケースをカバーします

Brian Goetz 最初のスレッドで、Collections.parallelStream()が他の並列ストリームファクトリメソッドが削除された後でも保持できるほど価値がある理由を説明します。

私たちにはありませんこれらのそれぞれの明示的な並列バージョンがあります[ストリームファクトリ];私たちは最初にそれを行い、APIの表面積を削減するために、APIから20以上のメソッドを削除することは.intRange(...).parallel()の表面の不快さとパフォーマンスコストのトレードオフに値するという理論に基づいてそれらをカットしました。しかし、私たちはコレクションでその選択をしませんでした。

Collection.parallelStream()を削除するか、すべてのジェネレーターの並列バージョンを追加するか、何もせずにそのままにすることができます。私はすべてがAPI設計の根拠で正当化できると思います。

現状に矛盾はあるものの、私は一種の現状が好きです。 2Nストリーム構築メソッドを使用する代わりに、N + 1を使用していますが、追加の1はすべてのコレクションに継承されるため、非常に多くのケースをカバーします。だから私は、なぜその余分な1のメソッドを持つことはそれだけの価値があるのか​​、そしてそれ以上行くことの不一致を受け入れることが受け入れられるのかを自分に正当化することができます。

他の人は同意しませんか?ここでN + 1[Collections.parallelStream()only]が現実的な選択肢ですか?それとも、Nの純粋さを求めるべきですか[Stream.parallel()に依存します]?または、2Nの利便性と一貫性[すべての工場の並行バージョン]?または、さらに優れたN + 3[Collections.parallelStream()と他の特殊なケース]がありますか?サポートする?

Brian Goetz は、Arrays.parallelStream()に関する後の説明でこの立場を支持します。

私は今でもCollection.parallelStreamが本当に好きです。発見の可能性に大きな利点があり、API表面積にかなり大きな利益をもたらします。もう1つの方法ですが、コレクションはストリームソースの本当に一般的なケースであるため、多くの場所で価値を提供します。

parallelStream()の方がパフォーマンスが高い

ブライアンゲッツ

直接バージョン[parallelStream()]は、ラッピングが少なくて済むという点でパフォーマンスが優れています(ストリームを並列ストリームにするには、最初に作成する必要があります順次ストリーム、次にその状態の所有権を新しいストリームに転送します。)

効果が重要であるかどうかについてのケビンBourrillionの懐疑論に応えて、 ブライアン再び

どれだけ真剣にカウントしているかによります。 Dougは、並列操作の途中で、個々のオブジェクトの作成と仮想呼び出しをカウントします。フォークを開始するまで、アムダールの法則の反対側にいるためです。これは、作業をフォークする前に発生するすべての「シリアル分数」です。これにより、損益分岐点がさらに押し出されます。したがって、並列処理のセットアップパスをすばやく取得することは重要です。

Doug Leaがフォローアップ 、しかし彼の立場をヘッジします:

並列ライブラリのサポートを扱う人々は、そのような事柄についてある程度の態度調整が必要です。間もなく一般的なマシンでは、並列処理のセットアップに無駄なサイクルが発生するたびに、64サイクルというコストがかかります。並列計算を開始するために64個のオブジェクトを作成する必要がある場合、おそらく別の反応があったでしょう。

とは言っても、APIが効率的な実装を除外しない限り、APIを改善するために実装者に一生懸命に働きかけることを私は常に完全にサポートしています。したがって、parallelStreamを強制終了することが本当に重要な場合は、stream().parallel()をビットフリップなどに変換する方法を見つけることができます。

確かに、Arrays.parallelStream()に関する後の説明 Stream.parallel()のコストが低いことに注意してください

stream()。parallel()のステートフルさが将来を複雑にします

説明の時点で、ストリームをシーケンシャルからパラレルに切り替えてから戻すことは、他のストリーム操作とインターリーブすることができます。 Doug Leaに代わってBrian Goetz は、順次/並列モード切り替えがJavaプラットフォームの将来の開発を複雑にする可能性がある理由を説明します:

理由を説明するのに最善を尽くします。これも(ステートフルメソッド(ソート、区別、制限)のように)好きではないため、従来のデータの観点からストリームパイプラインを表現することができないように、段階的に進めます。 -並列コンストラクト。これは、ベクトルプロセッサ、FPGA、GPUなど、私たちが作成するものに関係なく、それらを明日のコンピューティング基板に直接マップする機能をさらに制限します。

Filter-map-reduceマップは、あらゆる種類の並列計算基板に非常に明確に対応します。 filter-parallel-map-sequential-sorted-limit-parallel-map-uniq-reduceにはありません。

したがって、ここでのAPI設計全体は、ユーザーが表現したいと思うものを簡単に表現できるようにすることと、透過的なコストモデルで予測可能に高速化できる方法との間の多くの緊張を体現しています。

このモードの切り替えは さらなる議論の後に削除されました でした。ライブラリの現在のバージョンでは、ストリームパイプラインは順次または並列です。 sequential()/parallel()への最後の呼び出しが優先されます。この変更により、ステートフル性の問題を回避するだけでなく、parallel()を使用してシーケンシャルストリームファクトリから並列パイプラインをセットアップするパフォーマンスも向上しました。

ファーストクラスの市民としてparallelStream()を公開すると、ライブラリに対するプログラマーの認識が向上し、より優れたコードを書くようになります

Brian Goetz再びTim Peierlsの引数 に応じて、そのStream.parallel()により、プログラマは並列処理する前にストリームを順番に理解できます。

私はこの逐次的な直感の価値について少し異なる視点を持っています-私は、この全体的な取り組みの最大の課題である場合、広範に及ぶ「逐次的な期待」を1つと見なします。人々は常に不正な順次バイアスをもたらし、これにより、「要素を「だます」ための方法として1要素の配列を使用するなどの愚かなことをするようになります愚かな」コンパイラーに変更可能なローカルをキャプチャさせるか、ラムダを引数として使用して、計算中に(スレッドセーフではない方法で)使用される変換状態をマップし、次に、それらが何であるかを指摘したときやり直し、肩をすくめて、「ええ、でも私は並行してやっていない」と言います。

シーケンシャルストリームとパラレルストリームをマージするために、多くの設計トレードオフを行いました。結果はクリーンなものであり、ライブラリーが10年以上も使用できる可能性が高まると思いますが、これを並列バッグがくぎ付けになっているシーケンシャルライブラリーだと人々に思わせるのは特に好きではありません。側面上。

62
Jeffrey Bosboom