Я выполняю некоторые тесты производительности и заметил, что выражение LINQ, например
result = list.First(f => f.Id == i).Property
медленнее, чем
result = list.Where(f => f.Id == i).First().Property
Это кажется встречным интуитивным. Я бы подумал, что первое выражение будет быстрее, потому что оно может остановить итерирование по списку, как только предикат будет удовлетворен, тогда как я бы подумал, что выражение .Where()
может перебирать весь список перед вызовом .First()
on полученное подмножество. Даже если последнее делает короткое замыкание, оно не должно быть быстрее, чем использование 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);
}