8

コード例:

let foo1 (arr : int[]) = 
    for i = 0 to arr.Length-1 do
        arr.[i] <- i

let foo2 (arr : int[]) = 
    for i in [0..arr.Length-1] do
        arr.[i] <- i

この機能は(パフォーマンスの点で)互いに同等である必要があると思いました。しかし、ILリストを調べると、次のことがわかります。

最初の関数、15行、動的割り当てなし、try演算子なし、仮想呼び出しなし:

IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0011
// loop start (head: IL_0011)
    IL_0005: ldarg.0
    IL_0006: ldloc.0
    IL_0007: ldloc.0
    IL_0008: stelem.any [mscorlib]System.Int32
    IL_000d: ldloc.0
    IL_000e: ldc.i4.1
    IL_000f: add
    IL_0010: stloc.0

    IL_0011: ldloc.0
    IL_0012: ldarg.0
    IL_0013: ldlen
    IL_0014: conv.i4
    IL_0015: blt.s IL_0005
// end loop

IL_0017: ret

2つ目-ほぼ100行、多くの割り当て/割り当て解除、仮想関数の呼び出し、多くのtry/ Dispose

IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: ldc.i4.1
IL_0003: ldarg.0
IL_0004: ldlen
IL_0005: conv.i4
IL_0006: ldc.i4.1
IL_0007: sub
IL_0008: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [FSharp.Core]Microsoft.FSharp.Core.Operators/OperatorIntrinsics::RangeInt32(int32, int32, int32)
IL_000d: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [FSharp.Core]Microsoft.FSharp.Core.Operators::CreateSequence<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0012: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!!0> [FSharp.Core]Microsoft.FSharp.Collections.SeqModule::ToList<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: unbox.any class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>
IL_001e: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_0023: stloc.1
.try
{
    // loop start (head: IL_0024)
        IL_0024: ldloc.1
        IL_0025: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        IL_002a: brfalse.s IL_003e

        IL_002c: ldloc.1
        IL_002d: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
        IL_0032: stloc.3
        IL_0033: ldarg.0
        IL_0034: ldloc.3
        IL_0035: ldloc.3
        IL_0036: stelem.any [mscorlib]System.Int32
        IL_003b: nop
        IL_003c: br.s IL_0024
    // end loop

    IL_003e: ldnull
    IL_003f: stloc.2
    IL_0040: leave.s IL_005b
} // end .try
finally
{
    IL_0042: ldloc.1
    IL_0043: isinst [mscorlib]System.IDisposable
    IL_0048: stloc.s 4
    IL_004a: ldloc.s 4
    IL_004c: brfalse.s IL_0058

    IL_004e: ldloc.s 4
    IL_0050: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    IL_0055: ldnull
    IL_0056: pop
    IL_0057: endfinally

    IL_0058: ldnull
    IL_0059: pop
    IL_005a: endfinally
} // end handler

IL_005b: ldloc.2
IL_005c: pop
IL_005d: ret

私の質問は、F#コンパイラがなぜそれほど複雑なコードを使用するのfoo2かということです。なぜそれはIEnumerableとても些細なループを実装するためにを使用するのですか?

4

2 に答える 2

14

for2番目の例では、範囲式を使用すると、通常のループに変換されます。

let foo2 (arr : int[]) = 
    for i in 0..arr.Length-1 do
        arr.[i] <- i

と同等になりfoo1ます。

セクション6.3.12F#言語仕様の範囲式を引用します。

expr1 .. expr2 do expr3 doneのvarの形式のシーケンス反復式は、ループ式の単純なものとして作成されることがあります(§6.5.7)。

ただし、2番目の例は次のようになります。

let foo2 (arr : int[]) = 
    let xs = [0..arr.Length-1] (* A new list is created *)
    for i in xs do
        arr.[i] <- i

新しいリストを明示的に作成した場所。

于 2012-05-04T15:48:58.987 に答える
8

あなたが見ているのは、インデックスベースの列挙とベースの列挙を使用することの標準的な違いですIEnumerable(またはC#の用語ではforvs foreach)。

2番目のサンプルでは、​​式[0..arr.Length-1]はコレクションを作成しており、F#はIEnumerable<T>値を列挙するために使用しています。この列挙スタイルの一部には、IEnumerator<T>を実装するものの使用が含まれIDisposableます。表示されているtry / finallyブロックはIDisposable::Dispose、例外が発生した場合でも、列挙の最後にメソッドが呼び出されるようにするために生成されます。

F#は2番目の例を最初の例に最適化し、余分なオーバーヘッドをすべて回避できますか?彼らがこの最適化を行うことができる可能性があります。単純な数値範囲ではなく、基本的に範囲式を確認して、同等のforコードを生成します。

F#は2番目の例を最適化する必要があります。私の投票はノーです。このような機能は、外部からは些細なことのように見えることがよくありますが、実際にそれらを実装し、さらに重要なことに、それらを維持することは、かなり高価になる可能性があります。賢明なユーザーは、いつでもコードを標準forバージョンに戻し、IEnumerable<T>オーバーヘッドを回避できます(プロファイラーが問題であると明らかにした場合)。最適化を実装しないと、F#チームは他のすばらしい機能を実装できるようになります。

于 2012-05-04T15:49:04.790 に答える