ここで、関数型プログラミングが計算科学に利用できる理由、および利用すべき理由についての私の議論を示します。メリットは膨大で、短所はすぐになくなります。私の考えでは、1つの欠点しかありません。
Con:C / C ++ / Fortranでの言語サポートの欠如
少なくともC ++では、この欠点はなくなりました-C ++ 14/17が関数型プログラミングをサポートする強力な機能を追加したためです。自分でライブラリ/サポートコードを書く必要があるかもしれませんが、言語はあなたの友達になります。C ++で不変多次元配列を行いライブラリー:一例として、ここで(プラグ警告)であるhttps://github.com/jzrake/ndarray-v2は。
また、科学アプリケーションに焦点を合わせていませんが、C ++での関数型プログラミングに関する優れた本へのリンクもあります。
ここに私がプロだと思うものの要約があります:
長所:
面では正し、機能的なプログラムが明らかにされている、よく提起:彼らは正しく物理変数の最小限の状態、および機能を定義することを強制その進歩、その時点で前方の状態:
int main()
{
auto state = initial_condition();
while (should_continue(state))
{
state = advance(state);
side_effects(state);
}
return 0;
}
偏微分方程式(またはODE)を解くことは、関数型プログラミングに最適です。advance
現在のソリューションに純粋な関数()を適用して、次のソリューションを生成するだけです。
私の経験では、物理シミュレーションソフトウェアは全般的に、貧弱な状態管理に悩まされています。通常、アルゴリズムの各段階は、共有(事実上グローバルな)状態の一部で動作します。これにより、操作の正しい順序を保証することが困難または不可能になり、ソフトウェアがセグメンテーションフォールト、またはさらに悪いことに、コードをクラッシュさせずに科学の完全性を静かに損なう可能性のあるエラー用語に対して脆弱になります出力。物理シミュレーションで共有状態を管理しようとすると、マルチスレッド化も抑制されます。これは、スーパーコンピューターがより多くのコア数に移行し、MPIでのスケーリングが10万タスクを超えることが多いため、将来の問題です。対照的に、関数型プログラミングでは、不変性のために共有メモリの並列処理が簡単になります。
アルゴリズムの遅延評価により、関数型プログラミングのパフォーマンスも向上します(C ++では、これはコンパイル時に多くの型を生成することを意味します-多くの場合、関数のアプリケーションごとに生成されます)。ただし、仮想ディスパッチを排除するだけでなく、メモリアクセスと割り当てのオーバーヘッドを削減します。これにより、コンパイラは、アルゴリズムを構成するすべての関数オブジェクトを一度に確認して、アルゴリズム全体を最適化できます。実際には、CPUの使用とメモリの割り当てを最適化するために、評価ポイントのさまざまな配置(アルゴリズムの結果がメモリバッファにキャッシュされる)を実験します。これは、モジュールまたはクラスベースのコードで通常見られるものと比較して、アルゴリズムステージの局所性が高いため(以下の例を参照)、かなり簡単です。
機能プログラムは、物理状態を単純化する限り理解しやすくなります。それは彼らの構文があなたの同僚全員によって容易に理解できると言うことではありません!作者は、よく知られた関数を使用するように注意する必要があり、一般の研究者は、アルゴリズムを手続き的にではなく機能的に表現することに慣れる必要があります。制御構造が存在しないことは一部の人にとって不快なものになる可能性があることは認めますが、コンピューターでより質の高い科学を行うことができるように、私たちが未来に進むことを妨げるとは思いません。
以下はadvance
、ndarray-v2
パッケージを使用して有限ボリュームコードから調整されたサンプル関数です。to_shared
演算子に注意してください-これらは、以前言及した評価ポイントです。
auto advance(const solution_state_t& state)
{
auto dt = determine_time_step_size(state);
auto du = state.u
| divide(state.vertices | volume_from_vertices)
| nd::map(recover_primitive)
| extrapolate_boundary_on_axis(0)
| nd::to_shared()
| compute_intercell_flux(0)
| nd::to_shared()
| nd::difference_on_axis(0)
| nd::multiply(-dt * mara::make_area(1.0));
return solution_state_t {
state.time + dt,
state.iteration + 1,
state.vertices,
state.u + du | nd::to_shared() };
}