Я провожу некоторые тесты производительности и заметил, что выражение LINQ вроде
result = list.First(f => f.Id == i).Property
медленнее, чем
result = list.Where(f => f.Id == i).First().Property
Это кажется нелогичным. Я бы подумал, что первое выражение будет быстрее, потому что оно может прекратить итерации по списку, как только предикат будет удовлетворен, тогда как я бы подумал, что выражение .Where()
может перебирать весь список перед вызовом .First()
на результирующее подмножество. Даже если последний делает короткое замыкание, оно не должно быть быстрее, чем использование First напрямую, но это так.
Ниже приведены два действительно простых модульных теста, которые иллюстрируют это. При компиляции с оптимизацией в TestWhereAndFirst примерно на 30% быстрее, чем TestFirstOnly в .Net и Silverlight 4. Я пытался заставить предикат возвращать больше результатов, но разница в производительности такая же.
Может ли кто-нибудь объяснить, почему .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);
}