Znam różnicę między IQueryable a IEnumerable i wiem, że kolekcje są obsługiwane przez Linq To Objects za pośrednictwem interfejsu IEnumerable.Dlaczego IQueryable jest dwa razy szybszy niż IEnumerable przy korzystaniu z Linq To Objects
Co jest dla mnie zagadką, że zapytania są wykonywane dwukrotnie szybciej, gdy kolekcja jest konwertowana na IQueryable.
Niech l być wypełniony obiektu typu listy, zapytanie LINQ dwukrotnie raz za szybko, jeśli lista l przekształca się w IQueryable poprzez l.AsQueryable().
Napisałem prosty test z VS2010SP1 i .NET 4.0, który pokazuje to:
private void Test()
{
const int numTests = 1;
const int size = 1000 * 1000;
var l = new List<int>();
var resTimesEnumerable = new List<long>();
var resTimesQueryable = new List<long>();
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for (int x=0; x<size; x++)
{
l.Add(x);
}
Console.WriteLine("Testdata size: {0} numbers", size);
Console.WriteLine("Testdata iterations: {0}", numTests);
for (int n = 0; n < numTests; n++)
{
sw.Restart();
var result = from i in l.AsEnumerable() where (i % 10) == 0 && (i % 3) != 0 select i;
result.ToList();
sw.Stop();
resTimesEnumerable.Add(sw.ElapsedMilliseconds);
}
Console.WriteLine("TestEnumerable");
Console.WriteLine(" Min: {0}", Enumerable.Min(resTimesEnumerable));
Console.WriteLine(" Max: {0}", Enumerable.Max(resTimesEnumerable));
Console.WriteLine(" Avg: {0}", Enumerable.Average(resTimesEnumerable));
for (int n = 0; n < numTests; n++)
{
sw.Restart();
var result = from i in l.AsQueryable() where (i % 10) == 0 && (i % 3) != 0 select i;
result.ToList();
sw.Stop();
resTimesQueryable.Add(sw.ElapsedMilliseconds);
}
Console.WriteLine("TestQuerable");
Console.WriteLine(" Min: {0}", Enumerable.Min(resTimesQueryable));
Console.WriteLine(" Max: {0}", Enumerable.Max(resTimesQueryable));
Console.WriteLine(" Avg: {0}", Enumerable.Average(resTimesQueryable));
}
Uruchomienie tego testu (z woli numTests == 1 i 10) daje następujący wynik:
Testdata size: 1000000 numbers
Testdata iterations: 1
TestEnumerable
Min: 44
Max: 44
Avg: 44
TestQuerable
Min: 37
Max: 37
Avg: 37
Testdata size: 1000000 numbers
Testdata iterations: 10
TestEnumerable
Min: 22
Max: 29
Avg: 23,9
TestQuerable
Min: 12
Max: 22
Avg: 13,9
Powtórzenie testu, ale zmiana kolejności (tj. Pierwszy pomiar IQuerable, a następnie IEnumerable) daje wyniki różnicowania!
Testdata size: 1000000 numbers
Testdata iterations: 1
TestQuerable
Min: 75
Max: 75
Avg: 75
TestEnumerable
Min: 25
Max: 25
Avg: 25
Testdata size: 1000000 numbers
Testdata iterations: 10
TestQuerable
Min: 12
Max: 28
Avg: 14
TestEnumerable
Min: 22
Max: 26
Avg: 23,4
Oto moje pytania:
- Co robię źle?
- Dlaczego jest IEnumerable szybsza, jeśli test zostanie wykonany po teście IQueryable?
- Dlaczego numer IQueryable jest szybszy, gdy numer nie. serii testów jest zwiększona?
- Czy jest nałożona kara za pomocą IQueryable zamiast IEnumerable?
Zadaję te pytania, ponieważ zastanawiałem się, którego użyć do mojego interfejsu repozytorium. Teraz wysyłają zapytania do kolekcji w pamięci (Linq to Objects), ale w przyszłości ten może być źródłem danych SQL. Jeśli zaprojektuję klasy repozytoriów teraz z IQueryable, mogę bezboleśnie przejść później na Linq na SQL. Jednakże, jeśli występuje kara za wykonanie, a następnie przyklejenie się do IEnumerable, podczas gdy nie ma w tym przypadku SQL, wydaje się mądrzejszy.
Nie określasz, czy budujesz w trybie zwolnienia lub debugowania, i nie "wczytujesz" najpierw funkcji, aby uzyskać szum. (Myślę). Na dłuższą metę różnica kilku ms powyżej 10.000.000 iteracji nie wydaje się zbyt wielka. – asawyer
Budowałem w trybie debugowania. Przejście do trybu zwolnienia i dodanie uruchomienia "inicjalizacji" (tj. Wykonanie i zmaterializowanie każdego zapytania raz) pomogło: Teraz kod ** IEnumerable ** działa nieco szybciej niż ** kod IQueryable ** (11ms vs 12ms). I właśnie tego się spodziewałem. Więc mój testowy kod był wadliwy. Dzięki za wskazówki! – rbu
"Mogę bezboleśnie przełączyć się później na Linq na SQL" ... Bardzo chciałbym usłyszeć, jak to działa. –