2013-06-17 13 views
16

ja pisał do testowania wydajności wykorzystania foreach vs LINQ:Dlaczego LINQ szybciej w tym przykładzie

private class Widget 
{ 
    public string Name { get; set; } 
} 

static void Main(string[] args) 
{ 
    List<Widget> widgets = new List<Widget>(); 
    int found = 0; 

    for (int i = 0; i <= 500000 - 1; i++) 
     widgets.Add(new Widget() { Name = Guid.NewGuid().ToString() }); 

    DateTime starttime = DateTime.Now; 

    foreach (Widget w in widgets) 
    { 
     if (w.Name.StartsWith("4")) 
      found += 1; 
    } 

    Console.WriteLine(found + " - " + DateTime.Now.Subtract(starttime).Milliseconds + " ms"); 

    starttime = DateTime.Now; 
    found = widgets.Where(a => a.Name.StartsWith("4")).Count(); 

    Console.WriteLine(found + " - " + DateTime.Now.Subtract(starttime).Milliseconds + " ms"); 

    Console.ReadLine(); 
} 

uzyskać coś podobnego następujące dane wyjściowe:

 
31160 - 116ms 
31160 - 95 ms 

W każdym biegu, LINQ wyprzedza foreach o około 20%. Rozumiem, że metody rozszerzenia LINQ używały standardowego C# pod okładkami.

Dlaczego w tym przypadku LINQ jest szybszy?

EDIT:

Więc zmieniłem kod do używania stopera zamiast datetime i wciąż te same wyniki. Jeśli najpierw uruchomię kwerendę LINQ, moje wyniki pokażą, że LINQ jest o około 20% wolniejsze niż foreach. To musi być jakaś kwestia rozgrzewki JIT. Moje pytanie brzmi: jak mogę zrekompensować rozgrzewkę JIT w moim przypadku testowym?

+46

Czy próbowałeś odwrócenie kolejności testów? Możliwe, że obserwujesz czas JIT. Zwykle lepiej najpierw uruchomić test, aby rozgrzać system, * następnie * uruchomić go ponownie i ustawić czas. Ponadto użyj stopera. Zobacz http://ericlippert.com/tag/benchmarks/ –

+0

http://codereview.stackexchange.com/a/14200 –

+0

Jon, myślę, że masz rację. Jak mogę zmienić swój kod, aby odfiltrować czas JIT i uzyskać prawdziwe liczby? – Coltech

Odpowiedz

16

To dlatego, że nie masz rozgrzewki. Jeśli odwrócić swoje przypadki dostaniesz dokładnie taka opposit wynik:

31272 - 110ms 
31272 - 80 ms 

rozpocząć dodawanie rozgrzewkę i użyć stopera do lepszego czasu.

Running test z rozgrzewki:

 //WARM UP: 
     widgets.Where(a => a.Name.StartsWith("4")).Count(); 

     foreach (Widget w in widgets) 
     { 
      if (w.Name.StartsWith("4")) 
       found += 1; 
     } 

     //RUN Test 
     Stopwatch stopwatch1 = new Stopwatch(); 
     stopwatch1.Start(); 

     found = widgets.Where(a => a.Name.StartsWith("4")).Count(); 
     stopwatch1.Stop(); 

     Console.WriteLine(found + " - " + stopwatch1.Elapsed); 

     found = 0; 
     Stopwatch stopwatch2 = new Stopwatch(); 
     stopwatch2.Start(); 

     foreach (Widget w in widgets) 
     { 
      if (w.Name.StartsWith("4")) 
       found += 1; 
     } 
     stopwatch2.Stop(); 

     Console.WriteLine(found + " - " + stopwatch2.Elapsed); 

wynik:

31039 - 00:00:00.0783508 
31039 - 00:00:00.0766299 
+2

Zasadniczo przeformułowałeś komentarz Jona Skeeta (z mniejszymi szczegółami, "rozgrzewka" kontra JIT). Powinieneś dodać przykłady i więcej wyjaśnień ... – ken2k

+0

Dzięki. Będę używał stopera zamiast datetime. Czy możesz zdefiniować, co masz na myśli przez "rozgrzewkę"? – Coltech

+0

dodano test z kodem przykładowej rozgrzewki – Peter

2

zrobiłem niektóre profilowania jakiś czas temu, porównując następujące:

  • LINQ to Objects z/bez Regex

  • Lambda Expressions z/bez Regex

  • Tradycyjny iteracja z/bez Regex

Co mam Okazało się, że LINQ, Lambda i Tradycyjne iteracje były prawie zawsze takie same, ale prawdziwa różnica w czasie była w wyrażeniach Regex. Dopiero dodanie Regexa spowodowało spowolnienie oceny (dużo wolniej). (Szczegóły tutaj: http://www.midniteblog.com/?p=72)

To, co widzisz powyżej, prawdopodobnie wynika z faktu, że wykonujesz oba testy w tym samym bloku kodu. Spróbuj skomentować jedną z nich, zsynchronizuj ją, a następnie skomentuj drugą. Upewnij się także, że korzystasz z wersji release, a nie debuggera.

+0

Co to pytanie ma wspólnego z regex? – svick

+0

To tylko część przykładu profilowania. Zapraszam do zignorowania części Regex. – edtheprogrammerguy