1. ホーム
  2. c#

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

2023-11-21 05:31:15

質問

パフォーマンステストを行っていて、次のような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);
}

どのように解決するのですか?

where+firstの方がfirstより早いという同じ結果を得ました。

Jonが指摘したように、Linqは遅延評価を使用するので、パフォーマンスは両方の方法でほぼ同じになるはずです(実際そうなっています)。

Reflector を見ると、First は単純な foreach ループを使用してコレクションを反復処理しますが、Where には異なるコレクション タイプ (配列、リストなど) に特化したさまざまなイテレータがあります。 おそらく、これが Where に小さな利点を与えています。