関数型プログラミング言語には、コンパイル時の最適化を行う機会がありますか?


10

私は「実世界のための関数型プログラミング」という本を読んでいました。まず、命令型プログラミング言語と関数型プログラミング言語の比較から始めました。そして、関数型プログラミングの「値」と「式」が命令型プログラミングの「変数」と「関数」とどのように異なるかを述べました。ディスカッションから私は次のようなアイデアを開発しました-

関数型プログラミング言語には、対応する命令型言語よりもコンパイル時間の最適化を行う機会が多くあります。

本当ですか?

回答:


16

関数型プログラミング言語は、コンパイル時の最適化をはるかに上回ります。理由の1つは、純粋さです。状態がないため、同時実行は簡単です。そのため、コンパイラは2つの分岐を取り、プログラムの動作を変更せずにそれらを簡単に一致させることができます。

同時に、状態なしで計算できるもの(つまり、Haskellでモナドでないもの)は、コンパイラによって事前に計算できますが、そのような計算はコストがかかる可能性があるため、おそらく部分的にしか行われません。

さらに、計算に不要なものはすべて、プログラムから完全に最適化できます。


1
+1:副作用のないプログラミングは最適化が非常に簡単です。
S.Lott、2011

@mathepic:実際には(Haskellでの)並列化はコンパイル時と実行時の両方で行われます。コンパイル時は、指定したスレッドの数に応じて、シャード(スレッドシード)を作成する価値があるかどうか、およびランタイムがシャードをできるだけ処理するかどうかを決定します。単一のスレッドのみを指定した場合、シャードは作成されますが、シャードは次々に処理されます。
Matthieu M.11年

2
@mathepic:純度の別の使用->怠惰と計算の欠如。値が不要であることを証明できる場合は、計算を完全に削除します。必要な場合は、遅延サンクを使用してください。必要になることがわかっている場合は、直接計算してください(遅延のオーバーヘッドを回避するため)。純度は(ただ)驚くべきものです:)
Matthieu M.

@Matthieuの良い点
代替案

1
@Mass IOモナドでさえ純粋です。IOモナドを「実行」した結果はそうではありません。
代替

7

原則として、関数型言語には、対応する命令型のものよりもコンパイル時の最適化の可能性が高いというのは、おそらく本当です。

しかし、より興味深いのは、それらが現在のコンパイラーに実際に実装されており、これらの最適化が実際にどの程度関連しているか(つまり、演繹的な予測可能なコンパイラー設定での本番環境における慣用の「real life(TM)」コードの最終パフォーマンス)です。

たとえば、悪名高いコンピューター言語ベンチマークゲームのHaskellへの提出(悪いかもしれませんが、現時点ではそれほど優れていません)は、かなりの時間が費やされたように見えます手動による最適化は、「原因によるコンパイラの最適化の可能性」に関する主張に直面してinsert some property about FP languages here、最適化は関連する現実よりも(現在は少なくとも)理論的な可能性のように見えます。

私はこの点で間違っていると証明されて嬉しいです。


1
Haskellで手動による最適化を行う理由は、Haskellでは特定の「単純な」操作が(複雑さの観点から)多少時間がかかるということと関係があります。たとえば、リストの最後の要素を取得したいとします。Javaでは、これはかなり単純な操作です。Haskellでは、ナイーブなアプローチでは、リスト全体が最後までたどり着くまで(リストの面倒な性質のため)、O(n)操作になります。ここで(部分的に)手動で最適化を行います
。– mipadi

2
Haskellの手作業による最適化には正当な理由があると私は確信していますが、それらが「単純な」操作に必要であることは、コードを最適化するより大きな機会は(現在のところ)理論上のみ関連があるという印象を与えます。
Alexander Battisti

6
それは、アルゴリズムの最適化と生成コードの最適化の違いです。Cの例を見てみましょう。検索アルゴリズムを作成している場合、単純にリストをウォークスルーする単純なアルゴリズムを作成するか、バイナリ検索を使用できます。どちらの場合も、コンパイラはコードを最適化しますが、単純な検索アルゴリズムをバイナリ検索に変換しません。Haskellコードの手動による最適化の多くは、生成されたコードを最適化するのではなく、アルゴリズム自体を最適化することと関係があります。
mipadi

2
@mipadi、しかし、Haskellアルゴリズムの単純なバージョンは、他の言語のアルゴリズムの単純なバージョンほど良くないようです。(機能モデルとコンピュータアーキテクチャの不一致が原因だと思います)コードを生成するのは得意ですが、この問題を克服するには不十分です。
Winston Ewert

>>悪いかもしれない<<-悪いかどうか知っていますか?「悪い」とはどういう意味ですか?具体的にご記入ください。
igouy

2

関数型では、プログラムを介した値のフローは非常に非常にわかりやすくなっています(コンパイラとプログラマの両方にとって)。これにより、コンパイラは、値を格納する場所、値を保持する期間などを決定する余裕が大幅に広がります。

命令型言語では、コンパイラーは、ほとんどの変数が定義された存続期間中存続するメモリー内の実際の位置に対応するモデルをプログラマーに約束します。潜在的に、ステートメントはこれらの場所のいずれかから読み取り(または書き込み!)する可能性があるため、コンパイラーはメモリーの場所をレジスター割り当てで置き換えるか、2つの変数を単一のストレージ場所にマージするか、または場所の入念な分析を実行した後に同様の最適化を実行できますプログラム内では、その変数が参照される可能性があります。

ここで、2つの警告があります。

  • プログラミング言語コミュニティは、過去50年間、この分析を行うための巧妙な方法の開発に多くの努力を費やしました(無駄にしたのですか?)。ほとんどの場合、簡単なケースのいくつかを実行するために、レジスターカラーリングなどのよく知られたトリックがあります。しかし、これは大きくて遅いコンパイラと、コンパイルの複雑さと結果のコードの品質との一定のトレードオフをもたらします
  • 同時に、ほとんどの関数型プログラミング言語も純粋に関数型ではありません。何のコンパイラはこれらのトリックの完全に自由になることはできませんので、実際にI / Oに応えるように、行うために必要なもののプログラムの多くは、本質的に非機能的であり、何の言語はそれらを完全に回避できない-ビットであってもHaskellの、あまりにもを純粋に私の好みに合わせて(Your Mileage May Vary)は、コードの非機能部分のみを制御およびウォールオフでき、完全に回避することはできません。

しかし、一般的な質問に答えるために、はい、機能的パラダイムはコンパイラーを命令的な設定にはない最適化する多くの自由を与えます。


Haskellのすべてが機能しますmain。それは、状態そのものを使用するものではなく、状態変換関数であるということです。
代替案

はい、いいえ-概念的には、Haskellプログラムは、ユーザーとそのプログラムとのやり取り(およびシステムの乱数ジェネレーターの状態、現在のネットワークのレイテンシ、およびその他の本質的なもの)に対する純粋な機能であると言っても間違いありませんプログラムが応答する非機能的な入力)が、実際には違いのない区別です。
ジムワイズ、

@jimwise参照透過性は非常に大きな違いです。
代替

少なくともここで説明する目的のために、実際には持っていません。IOや乱数生成などの操作のポイントは、同じ入力で呼び出されるたびに異なる値返す必要があることです。これにより、プログラマーまたはコンパイラーのいずれかに対して、機能に関する推論が本質的に制限されます。
ジムワイズ、

1
@mathepicはい、もちろん、ランダム性やユーザー入力(またはネットワーク遅延、システム負荷)を状態から状態への無限のストリームまたは関数として概念的に見ることができますが、プログラムの動作に関する有用な推論に役立つビューではありませんあなたまたはあなたのコンパイラー-ボブ・ハーパーは、CMUの新しい関数型プログラミング初のCSカリキュラムに関する彼のブログの最近の投稿でこの点をうまくカバーしてい ます。
ジムワイズの
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.