robię kilka testów wydajności i zauważył, że wyrażenie LINQ jakDlaczego LINQ .Where (predicate) .First() jest szybszy niż .First (predykat)?
result = list.First(f => f.Id == i).Property
jest wolniejszy niż
result = list.Where(f => f.Id == i).First().Property
Wydaje licznik intuicyjne. Pomyślałbym, że pierwsze wyrażenie byłoby szybsze, ponieważ może zatrzymać zatrzymywanie iteracji po liście, gdy tylko predykat zostanie spełniony, podczas gdy ja bym pomyślał, że wyrażenie .Where()
może iterować na całej liście przed wywołaniem .First()
na wynikowym podzbiorze. Nawet jeśli ten ostatni ma zwarcie, nie powinien być szybszy niż pierwszy bezpośrednio, ale tak jest.
Poniżej przedstawiono dwa bardzo proste testy jednostkowe, które ilustrują to. Gdy kompilacja z optymalizacją na TestWhereAndFirst jest około 30% szybsza niż TestFirstOnly na .Net i Silverlight 4. Próbowałem, aby predykat zwrócił więcej wyników, ale różnica w wydajności jest taka sama.
Czy ktoś może wyjaśnić, dlaczego .First(fn)
jest wolniejszy niż .Where(fn).First()
? Widzę podobny wynik intuicyjny z .Count(fn)
w porównaniu do .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);
}
Jak się mierzysz? –
Twoja początkowa myśl jest jednak błędna: LINQ wykonuje leniwe obliczenia, więc gdy zostanie wywołane 'First()', zapyta (zwracana wartość) 'Where (...)' dla tylko jednego dopasowania i nigdy nie prosi o inny. Tak więc dokładnie ta sama liczba elementów zostanie zbadana, gdy nazwiesz 'First (...)' (tj. Bezpośrednio z predykatem). – Jon
Otrzymuję ten sam wynik, '.Where(). Pierwsze()' wynosi 0,021 sekundy, a '.First()' wynosi 0,037 sekundy. Jest to z prostą listą 'int's. – Ryan