2013-09-22 8 views
5

のコードとF#が生成するコードをseq{}のコードと比較すると、ユーザー定義のワークフローのコードとはかなり異なって実装されています。かつてのC#と同じように、その '反復子メソッド(iterator methods)'に使用されます。一方、ユーザー定義のワークフローでは、想定どおりに対応するBuilderオブジェクトを使用します。F#:seq {}と他の計算ワークフローとのILコードを生成

だから私は不思議です - その違いは何ですか?

これは歴史的な理由によるものですか。 「seqはワークフローの前にあった」
または、重要なパフォーマンスが得られますか?
その他の理由はありますか?

答えて

6

これは、F#コンパイラによって実行される最適化です。私が知る限り、実際には後で実装されました.F#コンパイラは最初にリスト内包を持っていましたが、計算式の汎用バージョン(seq { ... }でも使われていましたが)はそれほど効率が悪いので、後のバージョンで最適化が追加されました。

主な理由は、これによって多くの割り当てと迂回が削除されることです。計算式を使用している場合

seq { for i in input do 
     yield i 
     yield i * 10 } 

、これはのようなものに変換されます::

seq.Delay(fun() -> seq.For(input, fun i -> 
    seq.Combine(seq.Yield(i), seq.Delay(fun() -> seq.Yield(i * 10))))) 

機能割り当てのカップルとForループ常にラムダを呼び出す必要がありますのは、あなたが何かを持っているとしましょう関数。この最適化により状態マシン(C#ステートマシンに似ています)に変換されます。したがって、生成された列挙子のMoveNext()オペレーションは、クラスのいくつかの状態を変更してから復帰します。

パフォーマンスシーケンスのためのカスタム演算ビルダー:

type MSeqBuilder() = 
    member x.For(en, f) = Seq.collect f en 
    member x.Yield(v) = Seq.singleton v 
    member x.Delay(f) = Seq.delay f 
    member x.Combine(a, b) = Seq.concat [a; b] 
let mseq = MSeqBuilder() 
let input = [| 1 .. 100 |] 

今、私たちはこの(F#インタラクティブに#timeを使用して)テストすることができます。私のコンピュータ上で

for i in 0 .. 10000 do 
    mseq { for x in input do 
      yield x 
      yield x * 10 } 
    |> Seq.length |> ignore 

をカスタム012を使用した場合、これは2.644secを取りますビルダーがになりますが、組み込まれた最適化seqの式を使用した場合は0.065秒になります。そのため、最適化により、シーケンス式が大幅に効率化されます。

+0

カスタムビルダメソッドをインライン化して最適化することができます。 – t0yv0

+0

@TomasPetricek:MSeqBuilderを書き換えて、最適化された状態マシンのバージョンに近いコードを生成する方法はありますか? – user1411900

+0

@toyvoそれは大きなポイントです。それは速くなるはずです。 –

0

歴史的に、計算式(「ワークフロー」)は、シーケンス式の一般化であった。http://blogs.msdn.com/b/dsyme/archive/2007/09/22/some-details-on-f-computation-expressions-aka-monadic-or-workflow-syntax.aspx

しかし、答えは確かに大きな成果が得られることです。私は確かなリンクを張ることができませんが(http://blogs.msdn.com/b/dsyme/archive/2007/11/30/full-release-notes-for-f-1-9-3-7.aspxの「シーケンス式のフィルター」に関連する「最適化」に関する記述がありますが)、これはある時点で最適化されたことを思い出しています。時間。私は利益が自明であると言いたいと思います。シーケンス表現は「コア」言語機能であり、可能なあらゆる最適化に値するものです。

同様に、特定のテール再帰関数は、テールコールではなく、toループで最適化されることがわかります。

関連する問題