PLinq 拡張ステートメント AsParallel() を手動で追加する代わりに、コンパイラは自動的にこれを理解できなかったのでしょうか? コードがサポートしている場合、特に並列化を望まない例はありますか?
3 に答える
幸いなことに、コンパイラの研究者は、コンパイラを自動的に並列化することに本当に近いと言っています。悪いニュースは、彼らが50年間それを言っているということです。
C#などの不純な言語の問題は、通常、十分な並列処理がないことです。不純な言語では、人間にとって非常に困難であり、プログラムが2つのコードが相互にまたは環境と相互作用するかどうかおよびどのように相互作用するかを理解することはほとんど不可能です。
純粋な参照透過性の言語では、逆の問題が発生します。すべてが並列化可能ですが、スレッドのスケジューリングは単に単純に実行するよりも時間がかかるため、通常は意味がありません。
たとえば、純粋で参照透過性の関数型言語では、次のようなものがあります。
if a <= b + c
foo
else
bar
end
5つのスレッドを起動して、、、、を並列に計算し、a
次に、b
を計算し、最後に、を計算します。これは、両方ともすでに計算されている、またはの結果を破棄することを意味します。(これが機能的であることにどのように依存するかに注意してください。不純な言語では、単純にanの両方のブランチを計算してから、一方を破棄することはできません。両方が何かを印刷する場合はどうなりますか?それをどのように「印刷解除」しますか?)c
foo
bar
+
<=
if
foo
bar
if
しかし、、、、およびが本当に安価である場合a
、これらの5つのスレッドのオーバーヘッドは計算自体よりもはるかに大きくなります。b
c
foo
bar
自動並列化は、最初に思われるよりもトリッキーです。それは、「コードがそれをサポートしている場合」の部分です。次のようなものを検討してください
counter = 0
f(i)
{
counter = counter + 1
return i + counter
}
result = map(f, {1,2,3,4})
コンパイラがここでマップを並列化することを決定した場合、プログラムの実行ごとに異なる結果が得られる可能性があります。確かに、f にはグローバル変数があるため、実際にはこの方法での使用をサポートしていないことは明らかです。ただし、 f が別のアセンブリにある場合、コンパイラはそれを並列化することが安全でないことを認識できません。実行時にアセンブリ f をイントロスペクトして決定することもできますが、「イントロスペクションは十分に高速であり、動的並列化はこれを行うことによる利点を無効にしないほど高速ですか?」という問題になります。また、イントロスペクションが不可能な場合もf
あり、P/Invoked 関数である可能性があり、実際には並列化に完全に適している可能性がありますが、ランタイム/コンパイラーにはそれを知る方法がないため、それが不可能であると想定する必要があります。これは氷山の一角にすぎません。
要するに、それは可能ですが難しいため、魔法の実装と魔法の利点との間のトレードオフは、間違った方向に偏りすぎている可能性があります.
自動並列化の研究をしています。これは非常にトリッキーなトピックであり、多くの博士号の主題となっています。論文。静的コード分析と自動並列化は、Fortran などの言語で大きな成功を収めていますが、地域間の依存関係を分析するコンパイラの機能には限界があります。ほとんどの人は、潜在的な並列パフォーマンスの向上と引き換えに、コードの正確性の保証を犠牲にすることを厭わないため、コンパイラは並列マーカーを挿入する場所を非常に保守的にする必要があります。
結論: はい、コンパイラはコードを並列化できます。しかし、人間はそれをよりうまく並列化できることが多く、コンパイラーにマーカーを配置する場所を理解させることは、非常に非常に難しい場合があります。Mitosis 並列化コンパイラの背景研究や D-MPI 研究など、このテーマに関する研究論文は多数あります。