2011-12-29 30 views
60

私はいくつかのパフォーマンステストを行うと、なぜLINQ.Where(述語).First()は.First(述語)より高速ですか?

result = list.First(f => f.Id == i).Property 

のようなLINQの式はこれがカウンター直感的なようだ

result = list.Where(f => f.Id == i).First().Property 

よりも低速であることに気づいています。私は.Where()式は結果のサブセットに.First()を呼び出す前に、リスト全体を反復処理するかもしれないと思っているだろう一方で、私は、それができるだけ早く述語が成立しているとして、リストを反復処理を停止することができますので、最初の式が速いだろうと思っているだろう。後者が短絡したとしても、Firstを直接使用するよりも速くすべきではありませんが、そうです。以下は

はこれを説明する2つの本当に簡単ユニットテストです。 TestWhereAndFirstで最適化してコンパイルすると、.NetとSilverlight 4でTestFirstOnlyより約30%高速です。私は述部をより多くの結果を返すように試みましたが、パフォーマンスの差は同じです。 .First(fn).Where(fn).First()より遅い理由

いずれかを説明できますか?私は.Count(fn)と同様のカウンター直感的な結果を.Where(fn).Count()と比較して見る。

private const int Range = 50000; 

private class Simple 
{ 
    public int Id { get; set; } 
    public int Value { get; set; } 
} 

[TestMethod()] 
public void TestFirstOnly() 
{ 
    List<Simple> list = new List<Simple>(Range); 
    for (int i = Range - 1; i >= 0; --i) 
    { 
     list.Add(new Simple { Id = i, Value = 10 }); 
    } 

    int result = 0; 
    for (int i = 0; i < Range; ++i) 
    { 
     result += list.First(f => f.Id == i).Value; 
    } 

    Assert.IsTrue(result > 0); 
} 

[TestMethod()] 
public void TestWhereAndFirst() 
{ 
    List<Simple> list = new List<Simple>(Range); 
    for (int i = Range - 1; i >= 0; --i) 
    { 
     list.Add(new Simple { Id = i, Value = 10 }); 
    } 

    int result = 0; 
    for (int i = 0; i < Range; ++i) 
    { 
     result += list.Where(f => f.Id == i).First().Value; 
    } 

    Assert.IsTrue(result > 0); 
} 
+4

どのようにタイミングを取っていますか? –

+3

あなたの最初の考えは間違っています:LINQは遅延計算を行います。したがって、 'First()'が呼び出されたときに、ただ1つの一致に対して( 'の返り値)' Where(...)をクエリし、したがって、「First(...)」(つまり、述語で直接)を呼び出すときとまったく同じ数の要素が調べられます。 – Jon

+1

私は同じ結果を返します。 '.Where()。First()'は.021秒で '.First()'は.037秒です。これは 'int'の簡単なリストです。 – Ryan

答えて

43

私は同じ結果を得ました:+最初のものが最初のものより早かった。ジョンが述べたように

は、LINQのは、両方の方法のために広く同様の性能がなければならないので、遅延評価を使用して(となります)。

リフレクターで見ると、まず、コレクションを反復処理するための簡単なforeachループを使用していますが、イテレータの様々な異なるコレクション型(配列、リストなど)に特化しているところ。おそらく、これは小さな利点を与えるものです。

+0

私は怠惰な評価を知っています。私は完璧な怠惰な評価で+ FirstがFirstと全く同じパフォーマンスを与えると思っていたでしょう。 Reflectorでの調査は非常に便利です。ありがとうございます。これは確かにファースト・ワンだけがなぜより遅いのかを説明します。マイクロソフトがなぜ特殊なイテレータを使うのかだけを決めたのはなぜですか? – dazza

+0

@ user1120411:はい、しかし、最初(述語付き)とWhereは基本的に同じ方法を別の方法で実行することを選択するので、パフォーマンスが異なります。 – arx

+7

しかし、私がフレームワーク開発者であって、First(fn)を内部で返す場合、Where(fn).First()は、Firstの現在の実装とまったく同じように動作します。マイクロソフトの面倒を見ているようだ。 – dazza