web-dev-qa-db-ja.com

LINQ .Where(predicate).First()が.First(predicate)よりも速いのはなぜですか?

私はいくつかのパフォーマンステストを行っていますが、LINQ式が

_result = list.First(f => f.Id == i).Property
_

より遅い

_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);
}
_
69
dazza

私は同じ結果を得ました:where + firstは最初よりも高速でした。

Jonが述べたように、Linqは遅延評価を使用しているため、両方の方法でパフォーマンスはほぼ同じである(そしてそうである)。

Reflectorを見ると、最初は単純なforeachループを使用してコレクションを繰り返し処理しますが、Wh​​ereにはさまざまなコレクションタイプ(配列、リストなど)に特化したさまざまなイテレータがあります。おそらくこれがどこに小さな利点を与えるのでしょう。

50
arx