関数型プログラミングと科学計算


42

これがあいまいな質問であればおIびしますが、ここに行きます:

過去数年間、関数型プログラミングはソフトウェアエンジニアリングコミュニティで多くの注目を集めています。多くがScalaやHaskellなどの言語の使用を開始し、他のプログラミング言語やパラダイムよりも成功していると主張しています。私の質問は、高性能コンピューティング/科学コンピューティングの専門家として、関数型プログラミングに興味を持つべきですか?このミニ革命に参加すべきでしょうか?

SciComp作業領域における関数型プログラミングの長所と短所は何ですか?


2
意図的にストレートジャケットを着るのはなぜですか?副作用はツールです。実世界のアプリケーションには不可欠です。CPUとメモリの効率が必要な場合、関数型プログラミング言語は私のレーダーにはありません。自動化された検証/正確性チェックを必要とするプログラム(例えば、核施設で使用するために?)、それから大丈夫かもしれません。
実習キュー

回答:


34

私は関数型プログラミングをほんの少ししか行っていないので、この答えを一粒で取り上げてください。

長所:

  • 関数型プログラミングは非常に数学的に見えます。それはいくつかの数学的概念を表現するための素晴らしいパラダイムです
  • プログラムの正式な検証や定理証明などに利用できる優れたライブラリがあるため、プログラムについて推論するプログラムを書くことができます-この側面は再現性に優れています
  • PythonおよびC ++でラムダ式を使用して関数型プログラミングを実行できます。JuliaとMathematicaで関数型プログラミングを行うこともできます
  • 多くの人がそれを使用しないので、あなたは先駆者になることができます。MATLAB、Python、R、そして今ではジュリアの早期採用者がいたように、それを理解するには関数型プログラミングの早期採用者が必要です。

短所:

  • 通常、Haskell、OCaml(および他のML方言)、Lispなどの関数型プログラミング言語と考えられている言語は、一般に、パフォーマンスが重要な科学計算に使用される言語に比べて遅いと考えられています。OCamlは、せいぜいCの約半分の速度です。
  • これらの言語には、計算科学で一般的に使用される言語(Fortran、C、C ++、Python)と比較してライブラリインフラストラクチャがありません。PDEを解決したい場合は、計算科学で使用されていない言語よりも一般的に使用されている言語で行う方が簡単です。
  • 関数型プログラミング言語を使用する計算科学コミュニティは、手続き型言語を使用するほど多くはありません。つまり、学習やデバッグに多くの助けを得ることができず、おそらく人々はあなたにがらくたを与えるでしょうそれを使用する(あなたがそれに値するかどうか)
  • 関数型プログラミングのスタイルは手続き型プログラミングで使用されるスタイルとは異なります。手続き型プログラミングは通常、入門的なコンピューターサイエンスクラスおよび「MATLAB for Scientists and Engineers」タイプのクラスで教えられます。

「短所」セクションの異議の多くは克服できると思います。このStack Exchangeサイトでの一般的な議論のポイントであるように、開発時間は実行時間よりも重要です。関数型プログラミング言語が遅い場合でも、パフォーマンスが重要な部分をより高速な手続き型言語に委任でき、迅速なアプリケーション開発により生産性の向上を実証できる場合は、使用する価値があるかもしれません。ここで、純粋なPython、純粋なMATLAB、および純粋なRで実装されたプログラムは、C、C ++、またはFortranでのこれらの同じプログラムの実装よりもかなり遅いことに注意してください。Python、MATLAB、Rなどの言語は、実行速度と生産性を犠牲にしているという理由で人気があります。PythonとMATLABはどちらも、CまたはC ++でコンパイルされたコードへのインターフェイスを実装する機能を備えているため、パフォーマンスが重要なコードを実装して迅速に実行できます。ほとんどの言語には、Cへの外部関数インターフェイスがあります。これは、計算科学者が関心を持つほとんどのライブラリとインターフェイスするのに十分です。

関数型プログラミングに興味がありますか?

それはすべてあなたがクールだと思うものに依存します。あなたが慣習に逆らうことをいとわないタイプの人であり、あなたが関数型プログラミングで何をしたいのかという美徳について人々に福音宣教のスローガンを進んで進んで行くなら、私はそれのために行くと思います。否定論者のすべてが間違っていることを証明する以外の理由がない限り、計算科学の関数型プログラミングで人々がクールなことをするのを楽しみにしています(そして、否定論者がたくさんいるでしょう)。あなたが「どうして地獄で機能的なプログラミング言語を使用しているのですか(ここに彼らのお気に入りの手続き型プログラミング言語を挿入しますか?)」と尋ねる多くの人々に対処したいタイプの人ではないなら、私はそうしませんわざわざ。

シミュレーションを集中的に行うために、関数型プログラミング言語の使用がいくつかありました。量的商社のジェーン・ストリートは、金融モデリングとトレーディング戦略の実行にOCamlを使用しています。OCamlは、ライブラリで使用されるCコードを生成するためにFFTWでも使用されました。リストは、スタンフォードで開発され、PDEの解決に使用されるScalaで実装されたドメイン固有の言語です。関数型プログラミングは間違いなく業界で使用されます(必ずしも計算科学ではありません)。計算科学で飛ぶかどうかはまだ分からない。


4
ProとConの追加に貢献したいと思います。Pro ::コードの柔軟性:すべてが関数であるため、いつでもその関数を別の関数で呼び出すことができます。これは非常に強力です。Con ::コードの読みやすさ:関数型プログラミングコードは本当に読みにくいです。(ほとんど)それらを書いた人々のために。6か月前にMathematicaのBスプラインで一般的なPDEの問題を解決するために書いた古いコードを理解するのに少し時間がかかりました。同僚を怖がらせたいときは、常にそのコードを引き出します;-)。
seb

4
追加するのは、Con ::メモリ消費のみです。副作用をなくすには、多くのコピーを行う必要があります。
マシューエメット

1
@StefanSmith:(i)私はそれが研究で時々使われることを知っています(たとえば、MaximaはLispベースのCASです)。それを超えて、私は頭のてっぺんから知らない。(ii)わからない。私の答えの多くは、私が過去数年にわたって行ってきた会話から収集した事例証拠に基づいていました。
ジェフオックスベリー

@ seb、Haskellのような関数型言語にはほとんど当てはまらないLispのような関数型言語のプロパティを説明しているように聞こえます。
マークS.

1
@MatthewEmmettによるコメントに対する賛成票。コピーは、高性能の計算には非常に高価になる可能性があります。
チャールズ

10

科学的な計算のバックグラウンドを持ち、関数型プログラミング言語のユーザーであるHPC開業医であるため、これについて独自の視点を持っているかもしれません。HPCを科学計算と同一視したくありませんが、かなりの交差点があります。そのため、私はこれに答える際の観点です。

主にHPCユーザーと顧客が可能な限りピークパフォーマンスに近いことを真剣に考えているため、HPCで関数型言語が採用される可能性はほとんどありません。コードが機能的な方法で記述された場合、悪用される可能性のある並列処理が自然に公開されることは事実ですが、HPCでは十分ではありません。並列処理は、高いパフォーマンスを実現するためのパズルの一部にすぎません。また、さまざまなマイクロアーキテクチャの詳細を考慮する必要があります。これを行うには、通常、コードの実行を非常に細かく制御する必要があります。私が知っている関数型言語。

とはいえ、これが変わる可能性が高いと期待しています。私は、これらのマイクロアーキテクチャの最適化の多くが(ある程度)自動化できることを研究者が認識し始めている傾向に気付きました。これにより、ユーザーが実行したい計算の「仕様」を入力し、コンパイラーがCまたはFortranコードを出力するソースからソースへのコンパイラー技術の動物園が生まれました。ターゲットアーキテクチャを使用します。ちなみに、これは関数型言語の実行に適したものです。プログラミング言語のモデリングと分析です。関数型言語の最初の主要な採用者がコンパイラ開発者であることは偶然ではありません。いくつかの注目すべき例外を除き、これが実際にまだ定着しているのを見たことはありませんが、アイデアはそこにあります。


8

他の2つの答えに1つの側面を追加したいと思います。エコシステムは別として、関数型プログラミングは、マルチスレッドや分散コンピューティングなどの並列実行の素晴らしい機会を提供します。その固有の不変性の特性により、並列処理に適しています。これは、命令型言語に関しては一般的に*ブリープ*での大きな痛みです。

近年のハードウェアパフォーマンスの向上は、より高い周波数をプッシュするのではなく、プロセッサにコアを追加することに焦点を合わせているため、並列計算がより一般的になっています(ご存知のとおりです)。

Geoffが言及しているもう1つのことは、開発時間は実行時間よりも重要であることが多いということです。私は、計算集中型のSaaSを構築する会社で働いており、最初にC ++対Javaを試して、最初のパフォーマンステストを行いました。C ++はJavaの実行時間を約50%削減することがわかりました(これは計算ジオメトリ用であり、数値はアプリケーションによって異なる可能性が高い)が、開発者の時間の重要性からJavaを使用しました。最適化と将来のハードウェアパフォーマンスの改善は、市場に投入するのに役立ちます。別の方法で選択した場合、私たちはまだビジネスに参加していないと自信を持って言えます。

わかりましたが、Javaは機能的なプログラミング言語ではありません。そのため、Javaは何と関係があるのでしょうか、と尋ねるかもしれません。その後、関数型パラダイムの支持者を増やし、並列化の必要性につまずいたとき、システムの一部を段階的にScalaに移行しました。 Java。頭痛を最小限に抑えてシステムのパフォーマンスを向上させる際に非常に役立ち、将来のプロセッサにより多くのコアが詰め込まれた場合、ハードウェアビジネスのパフォーマンスがさらに向上することによるメリットを享受し続けるでしょう。

私は他の回答で言及された短所に完全に同意することに注意してください、しかし、並列実行の促進は、言及することができないほど強力なプロだと思いました。


8

Geoffは、彼のポイントの1つであるエコシステムを強調する以外に、私がほとんど追加しない理由の概要をすでに示しています。関数型プログラミングやその他のパラダイムを提唱している場合、対処しなければならない重要な質問の1つは、書き直さなければならない他のすべての上に構築できるソフトウェアの信じられないほどの量があることです。例としては、線形代数用のMPI、PETSc、Trilinos、または任意の有限要素ライブラリがあり、すべてCまたはC ++で記述されています。システムには非常に大きな慣性があります。おそらく、誰もがC / C ++が実際に計算ソフトウェアを書くのに最適な言語だと考えているからではなく、多くの人々が多くの人々。

ほとんどの計算ユーザーは、新しいプログラミング言語を試して、この問題に対する彼らの適合性を評価することに多くの価値があることに同意すると思います。しかし、他の人がやっていることと競争力のある結果を出すことができないので、それは困難で孤独な時間になるでしょう。また、別のプログラミングパラダイムへの次の移行を開始した人物としての評判が得られる場合もあります。ちょっと、Fortranを置き換えるのにC ++だけで約15年かかりました!


6
そして、C ++は、せいぜい、この分野でFortranを実際に置き換える方法の半分にすぎません。Fortranには常に新しいコードがあり、ブートするレガシーコードがたくさんあります!
ビル・バルト

2
C ++(Fortranとは異なります)は複雑すぎて、学習も使用もできません。新しいオープンソースの科学的コードは、Fortranでまだ書かれています。私の地域(地球科学)で注目に値するのは、PFlotran、SPECFEM3D、GeoFEMなどです。大気科学のほぼすべての新しいコードについても同様です。私見C ++は、置き換えられるはずだったものも置き換えていません(C)。
stali

1
Fortranを試してみる必要があります。Wolfgangは素晴らしい言語であり、簡単に学習/書き込みができ、速度はあなたを失望させません。
オンドレジ・セティク

3
私は速度を気にしません(まあ、私は少ししますが、それは他人のための包括的な考慮事項ではありません)。私にとって重要なのは、複雑なアルゴリズムをプログラムするのにどれだけ時間がかかるかということです。Fortranは、言語が非常に単純であるため、この点で負けています。言うべき標準ライブラリも、一般的なコードを許可するテンプレートもありません。Fortranは単に私の言語ではなく、率直に言って、他のほとんどすべての科学コンピューティングの人にとってもそうすべきではありません。
ヴォルフガングバンガース

4
@StefanSmith:はい。それは科学計算における防御可能なアイデアかもしれません(私はまだ時代遅れで非生産的だと私は主張します)。学生の教育に関する限り、それは確かに防御可能ではありません-私たちの学生の大半は学界を去り、業界では実際に誰もFortranを使用していないためです。
ヴォルフガングバンガース

7

簡単な要約は

  1. 数値計算では、可変性/副作用を使用して、ほとんどの高速化を実現し、割り当てを減らします(多くの関数型プログラミング構造には不変データがあります)
  2. 遅延評価は、数値コードで使用するのが難しい場合があります。
  3. パフォーマンスのために最下位レベルにドロップダウンすることが本当に重要なパッケージを開発している(C / Fortranまたは現在のジュリア)(これらでは必要に応じてアセンブラコードを編集することもできます)、またはこれらの高速ライブラリを使用するスクリプトを記述していますそのため、ほとんどの場合、開発時間を気にする傾向があります(そして、Julia / MATLAB / Python / Rを選択します)。関数型言語は、他の分野では役立つが、ここではあまり役に立たない奇妙な中間地点にある傾向があります。
  4. 微分方程式、最適化、数値線形代数などの数値アルゴリズムのほとんどは、収束に関して記述/開発/証明されています。つまり、近似あり、を取得する必要があります。これらのアルゴリズムを実装する自然なスタイルはループです。(いくつかのマルチグリッドアルゴリズムのように再帰的に記述されたアルゴリズムがいくつかありますが、これらははるかにまれです。)x n + 1xnxn+1

これらの事実により、関数型プログラミングはほとんどのユーザーには必要ないように思われます。


+1、ただしポイント3に1つ追加:高レベル言語の関数機能は非常に有用であり、他の回答で述べられている関数言語の利点の多く(簡単な並列化など)はこのシナリオに当てはまると思います。
サボルチ

3

計算科学における関数型プログラミングの使用は新しいものではないことに注意することは興味深いと思います。たとえば、1990年のこの論文では、部分評価を使用してLisp(おそらく最も初期の関数型プログラミング言語)で記述された数値プログラムのパフォーマンスを改善する方法を示しました。この作品は、1992年のGSI Sussman(SICPの名声)とJ Wisdom による、太陽系混otic とした挙動の数値的証拠を提供したツールチェーンの一部でした。その計算にかかわるハードウェアとソフトウェアに関する詳細はここで見つけることができます


1

Rは関数型言語であり、統計(および現在は機械学習)言語であり、実際には統計の第1言語です。ただし、HPC言語ではありません。物理シミュレーションなどの従来の「数値計算」には使用されません。しかし、機械学習の大規模な統計シミュレーション(MCMC)のために、大規模なクラスター(MPIなど)で実行することができます。

Mathematicaも関数型言語ですが、コアドメインは数値計算ではなくシンボリックコンピューティングです。

Juliaでは、関数型スタイルでプログラミングすることもできます(手続き型とOOのフレーバー(マルチディスパッチ)の隣)が純粋ではありません(基本データ構造はすべて可変です(タプルを除く)より重要なことは、手続き型よりもはるかに遅いため、あまり使用されないことです。

私はScalaを関数型言語ではなく、オブジェクトと機能のハイブリッドと呼びます。Scalaでは多くの機能概念を使用できます。Spark(https://spark.apache.org/)のため、Scalaはクラウドコンピューティングにとって重要です。

現代のFortranには実際に関数型プログラミングの要素がいくつかあることに注意してください:厳密なポインターセマンティクス(Cとは異なります)、純粋な(副作用なし)関数を使用でき(&マークを付けて)、不変性を保持できます。マトリックスインデックスの条件を指定できるスマートインデックスもあります。これは、C#のLINQのRや関数型言語の高次フィルター関数などの高レベル言語でのみ使用されるクエリです。そのため、Fortranはそれほど悪くはなく、多くの言語には見られない非常に最新の機能(co-arrayなど)を備えています。実際、Fortranの将来のバージョンでは、オブジェクト指向機能ではなく、より機能的な機能が追加されることを望んでいます(現在は通常です)。Fortranのオブジェクト指向は本当に厄介で見苦しいからです。


1

Proは、各機能言語に組み込まれた「ツール」です。データのフィルタリングが非常に簡単で、データの繰り返しが非常に簡単で、問題に対する明確で簡潔なソリューションを見つけるのが非常に簡単です。

唯一の短所は、この新しい種類の考え方に頭を悩ませる必要があるということです。あなたが知る必要があることを学ぶのに時間がかかる可能性があります。SciCompドメインの他のユーザーは実際にこれらの言語を使用していません。つまり、それほど多くのサポートを得ることができません:(

機能科学言語に興味があるなら、https://ac1235.github.ioを開発しました


1

ここで、関数型プログラミング計算科学に利用できる理由、および利用すべき理由についての私の議論を示します。メリットは膨大で、短所はすぐになくなります。私の考えでは、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の使用とメモリの割り当てを最適化するために、評価ポイントのさまざまな配置(アルゴリズムの結果がメモリバッファにキャッシュされる)を実験します。これは、モジュールまたはクラスベースのコードで通常見られるものと比較して、アルゴリズムステージの局所性が高いため(以下の例を参照)、かなり簡単です。

機能プログラムは、物理状態を単純化する限り理解しやすくなります。それは彼らの構文があなたの同僚全員によって容易に理解できると言うことではありません!作者は、よく知られた関数を使用するように注意する必要があり、一般の研究者は、アルゴリズムを手続き的にではなく機能的に表現することに慣れる必要があります。制御構造が存在しないことは一部の人にとって不快なものになる可能性があることは認めますが、コンピューターでより質の高い科学を行うことができるように、私たちが未来に進むことを妨げるとは思いません。

以下はadvancendarray-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() };
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.