Przeglądanie sekcji aplikacji webowej Pracuję dzisiaj z profilerem wydajności. Myślałem, że Unia powoduje pewne opóźnienia, ale zamiast tego znalazła inne zaskakujące wyniki.Wydajność FirstOrDefault()
Jedną z przyczyn spowolnienia okazała się FirstOrDefault.
Było bardzo prostej kwerendy LINQ, który wyglądał tak:
foreach(Report r in reports)
IDTOStudy study = studies.FirstOrDefault(s => s.StudyID == r.StudyID);
stworzyłem małą sposób powielać zachowanie Pomyślałem FirstOrDefault robi.
private IDTOStudy GetMatchingStudy(Report report, IList<IDTOStudy> studies)
{
foreach (var study in studies)
if (study.StudyID == report.StudyID)
return study;
return null;
}
Metoda ta otrzymuje FirstOrDefault wyglądać tak:
foreach(Report r in reports)
IDTOStudy study = GetMatchingStudy(r, studies);
Patrząc na nowy kod uruchomiony z profilera wydajności pokazał FirstOrDefault
wziąć dwa razy tak długo, jak zakończyć moją nową metodą. To był szok, aby zobaczyć.
Muszę zrobić coś nieprawidłowego z zapytaniem FirstOrDefault()
. Co to jest?
Czy FirstOrDefault()
ukończyć całe zapytanie, a następnie podjąć pierwszy element?
Jak mogę przyspieszyć i użyć FirstOrDefault()
?
Edit 1:
Jeden dodatkowy punkt zauważyłem jest to, że profiler mówi mi maxing mojego procesora na obu tych wdrożeń. To także coś, czego nie obchodzi mnie i nie oczekiwałem. Dodatkowa metoda, którą dodałem, nie zmniejszyła tego skoku, tylko skróciła jego czas trwania o połowę.
Edit 3:
Wykorzystanie badań w słowniku poprawiła czas pracy ogromnie. Zdecydowanie będzie wyglądać kod zatwierdzony. Nie odpowiada jednak na pytanie FirstOrDefault.
Edit 2:
Oto wnioskowana w prosty app konsoli przykładowy kod. Mój bieg nadal pokazuje, że w większości przypadków FirstOrDefault trwa dłużej.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Diagnostics;
namespace TestCode
{
public class Program
{
public List<IntHolder> list;
public static void Main(string[] args)
{
var prog = new Program();
prog.list = new List<IntHolder>();
prog.Add50000Items();
prog.list.Add(new IntHolder() { Num = 12345 });
prog.Add50000Items();
var stopwatch = new Stopwatch();
stopwatch.Start();
prog.list.FirstOrDefault(n => n.Num == 12345);
stopwatch.Stop();
Console.WriteLine("First run took: " + stopwatch.ElapsedTicks);
var lookingFor = new IntHolder() { Num = 12345 };
stopwatch.Reset();
stopwatch.Start();
prog.GetMatching(lookingFor);
stopwatch.Stop();
Console.WriteLine("Second run took: " + stopwatch.ElapsedTicks);
Console.ReadLine();
}
public void Add50000Items()
{
var rand = new Random();
for (int i = 0; i < 50000; i++)
list.Add(new IntHolder() { Num = rand.Next(100000) });
}
public IntHolder GetMatching(IntHolder num)
{
foreach (var number in list)
if (number.Num == num.Num)
return number;
return null;
}
}
public class IntHolder
{
public int Num { get; set; }
}
}
Jeśli używasz LINQ do SQL lub EF, następnie 'FirstOrDefault()' 'powinien po prostu wygenerować TOP 1 ' Zapytanie –
jakie są badania, czy jest to obiekt orm (np. dbset z Entity Framework lub podobnego)? –
@lazyberezovsky podejrzewam, że pierwszy przykład generuje O (n) i drugi O (1) zapytań db –