2013-06-17 11 views
16

ich die Leistung mit foreach vs LINQ folgendes zu testen schrieb:Warum ist LINQ schneller in diesem Beispiel

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(); 
} 

ich so etwas wie folgende Ausgabe:

 
31160 - 116ms 
31160 - 95 ms 

In jedem Lauf, LINQ outperforms foreach um etwa 20%. Es war mein Verständnis, dass die LINQ-Erweiterung Methoden Standard C# unter den Abdeckungen verwendet.

Warum ist LINQ in diesem Fall schneller?

EDIT:

Also änderte ich meinen Code Stoppuhr zu verwenden, anstatt von Datetime und immer noch die gleichen Ergebnisse zu erhalten. Wenn ich zuerst die LINQ-Abfrage ausführe, zeigen meine Ergebnisse, dass LINQ ungefähr 20% langsamer ist als foreach. Dies muss eine Art JIT-Warmnup-Problem sein. Meine Frage ist, wie kann ich JIT Aufwärmen in meinem Testfall kompensieren?

+46

Haben Sie versucht, die Reihenfolge der Tests Umkehren? Möglicherweise sehen Sie JIT-Timing. Es ist im Allgemeinen besser, den Test einmal zu starten, um das System aufzuwärmen, * dann * führe es erneut aus und takte es ab. Verwenden Sie auch Stoppuhr. Siehe http://ericlippert.com/tag/benchmarks/ –

+0

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

+0

Jon, ich denke du hast Recht. Wie kann ich meinen Code ändern, um das JIT-Timing herauszufiltern und reelle Zahlen zu erhalten? – Coltech

Antwort

16

Es ist, weil Sie kein Aufwärmen haben. Wenn Sie Ihre Fälle umkehren erhalten Sie excatly das opposit Ergebnis:

31272 - 110ms 
31272 - 80 ms 

Starten Sie ein Warm-up Hinzufügen und eine Stoppuhr für ein besseres Timing verwenden.

Durchführung des Tests mit Warm-up:

 //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); 

Ergebnis:

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

Sie haben Jon Skeets Kommentar (mit weniger Details, "Warmup" vs JIT) grundlegend umformuliert. Sie sollten Beispiele und weitere Erklärungen hinzufügen ... – ken2k

+0

Danke. Ich werde Stoppuhr anstelle von Datetime verwenden. Kannst du definieren, was du mit "Aufwärmen" meinst? – Coltech

+0

hinzugefügt Test mit Warmup-Beispielcode – Peter

2

Ich habe einige eine Weile zurück Profilierung, einen Vergleich der folgenden:

  • LINQ to Objects mit/ohne Regex

  • Lambda-Ausdrücke mit/ohne Regex

  • Traditionelle Iteration mit/ohne Regex

Was ich Es stellte sich heraus, dass LINQ, Lambda und die traditionelle Iteration fast immer gleich waren, aber der eigentliche Zeitunterschied lag in den Regex-Ausdrücken. Das Hinzufügen des Regex hat die Auswertung langsamer gemacht (viel langsamer). (Details hier: http://www.midniteblog.com/?p=72)

Was Sie oben sehen, liegt vermutlich daran, dass Sie beide Tests im selben Codeblock durchführen. Versuchen Sie, einen zu kommentieren, ihn zu timpfen und dann den anderen zu kommentieren. Stellen Sie außerdem sicher, dass Sie einen Release-Build ausführen, nicht im Debugger.

+0

Was hat diese Frage mit Regex zu tun? – svick

+0

Es ist nur ein Teil des Profilierungsbeispiels. Fühlen Sie sich frei, den Regex-Teil zu ignorieren. – edtheprogrammerguy

Verwandte Themen