Javacのようなコンパイラは、純粋な関数を自動的に検出して並列化しますか?


12

純粋な関数は、並列化を容易にすることが知られています。 本質的に並列実行に適応させる関数型プログラミングについてはどうですか?

Javacなどのコンパイラは、メソッドが純粋な関数であるかどうかを検出するのに十分スマートですか?Functionなどの機能インターフェイスを実装するクラスを常に実装できますが、副作用があります。


7
問題は、コンパイラが関数が純粋であるかどうかを知ることができるかどうかだけでなく、純粋な関数の並列実行をインテリジェントにスケジュールできるかどうかでもあります。スレッドごとに新しいスレッドを起動するだけでは不十分です。これは非効率的です。GHC(Haskell)は、怠inessと「グリーンスレッド」を使用してこれを処理します。純粋なスレッドがメインの不純なスレッドに対して正しくスケジュールされていることを確認することの追加の難しさを考えると、不純な言語が試みられた場合、私は正直に驚くでしょう。
ライアンライク

@ RyanReich、Javaなどの不純な関数型言語で関数型プログラミングを使用することでパフォーマンスが向上しますか?関数型プログラミングの利点は、モジュール性などの純粋に機能的なものですか?
ナビーン

@RyanReich GHCは、並列処理が必要なときにプログラマーに注釈を付けることで問題を処理します。純度とは、これらのアノテーションがセマンティクスを変えることはなく、パフォーマンスを変えることを意味します。(並行性を引き起こす可能性のある並行性メカニズムもありますが、これは魚の別のケトルです。)
デレクエルキンズはSEを去りました

@Naveen最適化に関しては、より自由度の高いコードの並べ替え、メモ化、一般的な部分式の削除など、最適化に関して純粋な関数には他の利点もあります。私は間違っている可能性がありますが、javacが純粋なコードを検出しようとするのを疑います。これはおそらく慣用的なコードではかなりまれであり、最も些細な場合を除いて多少難しいからです。たとえば、が存在しないことを知る必要がありますNullPointerException。これに基づく最適化の利点は、通常のJavaアプリケーションではおそらくかなり小さいでしょう。
デレクエルキンズは、

6
javacは、javaソースコードを受け取り、javaバイトコードクラスファイルを生成するjavaコンパイラです。それが何ができるか(そしてそうされるべきか)に関してかなり制約されています。バイトコードクラスファイルに並列性を導入するための自由度または必要な基礎メカニズムはありません。
エリックエイド

回答:


33

メソッドが純粋な関数であるかどうかを検出するのに十分なJavacなどのコンパイラーです。

「十分にスマート」という問題ではありません。これは純度分析とれ、一般的なケースでは不可能であることが証明されています。つまり、停止問題を解決することと同等です。

もちろん、オプティマイザーは常に不可能なことを常に実行します。「一般的なケースではおそらく不可能」とは、機能しないことを意味するのではなく、すべての場合に機能しないことを意味します。そのため、実際には関数が純粋かどうかをチェックするアルゴリズムがあります。結果が「わからない」になることはほとんどありません。つまり、安全性と正確さの理由から、仮定する必要があります。この特定の機能は不純かもしれません。

でも、それは例にない仕事を、アルゴリズムが複雑で高価です。

だから、それは 問題#1です:特別な場合にのみ機能します。

問題#2:ライブラリ。関数を純粋にするために、関数は純粋関数のみを呼び出すことができます(そして、それらの関数は純粋関数のみを呼び出すことができますなど)。Javacは明らかにJavaについてのみ知っており、見ることができるコードについてのみ知っています。したがって、関数が別のコンパイル単位で関数を呼び出す場合、それが純粋であるかどうかを知ることはできません。別の言語で書かれた関数を呼び出す場合、あなたは知ることができません。まだインストールされていないかもしれないライブラリ内の関数を呼び出す場合、あなたは知ることができません。等々。

これは、プログラム全体の分析があり、プログラム全体が同じ言語で記述されており、すべてが一度にコンパイルされる場合にのみ機能します。ライブラリを使用することはできません。

問題#3:スケジューリング。どの部分が純粋であるかを把握したら、それらを別々のスレッドにスケジュールする必要があります。か否か。スレッドの開始と停止は非常に高価です(特にJavaの場合)。スレッドプールを保持し、それらを開始または停止しない場合でも、スレッドコンテキストの切り替えは高価です。スケジュールとコンテキストの切り替えにかかる時間よりもかなり長く計算が実行されることを確認する必要があります。そうしないとパフォーマンスが低下しますが向上しません。

おそらく今ご想像のとおり、計算にかかる時間を計算することは一般的なケースでは不可能であることが証明されており(時間がかかるのは言うまでもなく、有限の時間がかかるかどうかもわかりません)、特別な場合。

余談:Javacと最適化。javacのほとんどの実装は、実際には多くの最適化を実行しないことに注意してください。たとえば、Oracleのjavacの実装は、最適化を行うために基礎となる実行エンジンに依存しています。これは別の問題につながります。たとえば、javacは特定の関数が純粋であり、十分に高価であると判断したため、別のスレッドで実行されるようにコンパイルします。次に、プラットフォームのオプティマイザー(HotSpot C2 JITコンパイラーなど)が登場し、関数全体を最適化します。今、あなたは何もしていない空のスレッドを持っています。または、javacが別のスレッドで関数をスケジュールすることを決定し、プラットフォームオプティマイザー スレッドの境界を越えてインライン化を実行できないことを除いて、完全に最適化します。したがって、完全に最適化できた関数は不必要に実行されます。

したがって、このようなことは、単一のコンパイラーで最適化のほとんどを一度に行う場合にのみ意味があり、コンパイラーは、さまざまなレベルでのさまざまな最適化とそれらの相互作用を認識し、活用することができます。

たとえば、HotSpot C2 JITコンパイラーは実際に自動ベクトル化を実行ますが、これも自動並列化の一種です。


「純粋関数」の定義によっては、実装での不純関数の使用が許可される場合があります。

@Deduplicatorまあ、あなたの定義に応じてdefinition、異なる使用して、definitionのはpurityおそらく曖昧である

1
あなたの問題#2は、ほとんどすべての最適化がJITによって実行されるという事実によってほとんど無効になります(明らかに知っていますが、無視してください)。同様に、JITはインタープリターによって収集された統計に大きく依存しているため、問題3は部分的に無効になります。特に、「最適化が最適化されていないため、「ライブラリを使用できません」ということに反対します。追加された複雑さが問題になることに同意します。
-maaartinus

2
@maaartinus:それに、私の答えの最後だけがjavacに固有のものです。私は特にやる「これはのみ動作し、あなたがプログラム全体が同じ言語で書かれている、とすべてが一度に一度にコンパイルされ、プログラム全体の解析を、持っているとき。」という、例えば、言及を これは明らかにC2にも当てはまります。1つの言語(JVMバイトコード)のみを扱い、プログラム全体に一度にアクセスできます。
ヨルグWミットタグ

1
@JörgWMittagOPがjavacについて尋ねることは知っていますが、javacが最適化の原因であると仮定しているに違いありません。そして、彼らはC2があることをほとんど知りません。私は言っていない、あなたの答えは悪いです。これは、javacのがそうさせることだけだ任意の(使用してのような些事を除いて最適化することはStringBuilder、私はそれを却下し、単に仮定したいので、OPは、javacの書き込みを行うが、ホットスポットを意味し、)非感覚です。あなたの問題#2は、最適化に対してかなり良い理由でのjavacでは。
-maaartinus

5

賛成の答えは、一つのことに注意することに失敗しました。スレッド間の同期通信は非常に高価です。関数が1秒あたり数百万回の呼び出しの速度で実行できる場合、実際には、そのままにするのではなく、並列化するのがさらに難しくなります。

アトミック変数を使用したビジーループを使用した最速の同期スレッド間通信は、残念ながらエネルギー効率が悪いです。条件変数を使用してエネルギーを節約する必要がある場合、スレッド間通信のパフォーマンスが低下します。

そのため、コンパイラは関数が純粋であるかどうかを判断するだけでなく、関数の実行時間を推定して、並列化が最終的な成果であるかどうかを確認する必要もあります。また、アトミック変数または条件変数を使用してビジーループを選択する必要があります。そして、背中の後ろにスレッドを作成する必要があります。

スレッドを動的に作成する場合、条件変数を使用するよりもさらに遅くなります。そのため、コンパイラは、すでに実行されている一定数のスレッドをセットアップする必要があります。

したがって、あなたの質問に対する答えは「いいえ」です。コンパイラは、特にJavaの世界で純粋な関数を自動並​​列化するのに十分な「スマート」ではありません。それらは自動並列化しないので賢いです!


5
自動並列化しないことで賢い!:これは行き過ぎです。一般的に、あらゆる点で並列化すること自体が非効率的であることは事実ですが、スマートコンパイラーは実用的な並列化戦略を特定します。ほとんどの人がこれを理解していると思うので、自動並列化について話すとき、私たちは自動実践的並列化を意味します。
ナット

@Nat:とんでもなく難しすぎる。これには、数百ミリ秒のランタイムスケールで純粋な関数を識別する必要があり、コンパイラーが繰り返しに定数を持たないループのランタイム(および必要ない場合)のアイデアを得ることを期待するのはばかげています。
ジョシュア

私は同意します-@Natのコメントは、並列化が必ずしも複数のスレッドを意味するわけではないことを示唆しています。たとえば、JITは純関数への複数の呼び出しをインライン化し、特定の場合にCPU命令をインターリーブすることができます。たとえば、両方のメソッド呼び出しで定数を取得する場合、一度取得して、使用するメソッドの両方のインスタンスのCPUレジスタに保持することができます。最新のCPUは、コードを最適化する際に非常に役立つ可能性のある多数の汎用レジスターと特殊な命令を備えた獣です。

1
@Joshua:JITコンパイラーの方がはるかに簡単です。JITコンパイラーは、関数が純粋ではない可能性があることを把握することもできますが、これまでの呼び出しでは不純な動作は呼び出されませんでした。
-gnasher729

@Joshuaに同意します。並列化が難しいアルゴリズムを使用しています。私は、いくつかの単純化された近似(およびアルゴリズムの変更)を行っても手動で並列化しようとしましたが、毎回悲惨な失敗を繰り返してきました。何かを並列化することが可能かどうかを示すプログラムでさえ、実際に並列化するよりもはるかに簡単ですが、非常に困難です。チューリング完全プログラミング言語について話していることを思い出してください。
-juhist
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.