2009-08-19 8 views
5

Dies ist eher eine akademische Frage zur Leistung als ein realistisches "Was soll ich verwenden?", Aber ich bin neugierig, da ich mich nicht viel mit ILs beschäftige, um zu sehen, was konstruiert ist und auf dem ich keinen großen Datensatz habe Hand zum Profil gegen.Was ist schneller in einer Schleife: eine Eigenschaft zweimal aufrufen oder die Eigenschaft einmal speichern?

So, das ist schneller:

List<myObject> objs = SomeHowGetList(); 
List<string> strings = new List<string>(); 
foreach (MyObject o in objs) 
{ 
    if (o.Field == "something") 
     strings.Add(o.Field); 
} 

oder:

List<myObject> objs = SomeHowGetList(); 
List<string> strings = new List<string>(); 
string s; 
foreach (MyObject o in objs) 
{ 
    s = o.Field; 
    if (s == "something") 
     strings.Add(s); 
} 

Beachten Sie, dass ich will nicht wirklich die Auswirkungen auf die Leistung der string.Add (n) wissen (wie was auch immer ausgeführt werden muss, kann nicht wirklich geändert werden), nur der Leistungsunterschied zwischen der Einstellung jeder Iteration (sagen wir mal, dass s irgendein primitiver Typ oder String sein kann) und dem Aufruf des Getter auf dem Objekt bei jeder Iteration.

+19

Warum fragen Sie _us_? Du hast den Code geschrieben. Stoppen Sie eine Stoppuhr, laufen Sie eine Milliarde Male in beide Richtungen, und dann wissen Sie, was schneller ist. –

Antwort

9

Ihre erste Option ist spürbar schneller in meinen Tests. Ich bin so ein Flip Flopper!Im Ernst, einige Kommentare wurden über den Code in meinem ursprünglichen Test gemacht. Hier ist der aktualisierte Code, der zeigt, dass Option 2 schneller ist.

class Foo 
    { 
     public string Bar { get; set; } 

     public static List<Foo> FooMeUp() 
     { 
      var foos = new List<Foo>(); 

      for (int i = 0; i < 10000000; i++) 
      { 
       foos.Add(new Foo() { Bar = (i % 2 == 0) ? "something" : i.ToString() }); 
      } 

      return foos; 
     } 
    } 

    static void Main(string[] args) 
    { 

     var foos = Foo.FooMeUp(); 
     var strings = new List<string>(); 

     Stopwatch sw = Stopwatch.StartNew(); 

     foreach (Foo o in foos) 
     { 
      if (o.Bar == "something") 
      { 
       strings.Add(o.Bar); 
      } 
     } 

     sw.Stop(); 
     Console.WriteLine("It took {0}", sw.ElapsedMilliseconds); 

     strings.Clear(); 
     sw = Stopwatch.StartNew(); 

     foreach (Foo o in foos) 
     { 
      var s = o.Bar; 
      if (s == "something") 
      { 
       strings.Add(s); 
      } 
     } 

     sw.Stop(); 
     Console.WriteLine("It took {0}", sw.ElapsedMilliseconds); 
     Console.ReadLine(); 
    } 
+0

Hey, +1 für das Schreiben eines Tests! Ich würde dir +2 geben, wenn ich könnte. –

+0

Andy: Auf welcher Runtime/Plattform hast du getestet? Ist es ein Release-Build? –

+0

Für mich ist das Ergebnis: 2294, 702 was Ihrer Schlussfolgerung widerspricht. –

7

Die meiste Zeit sollte Ihr zweites Code-Snippet mindestens so schnell wie das erste Snippet sein.

Diese beiden Codefragmente sind nicht funktional gleichwertig. Es wird nicht garantiert, dass Eigenschaften das gleiche Ergebnis über einzelne Zugriffe zurückgeben. Daher kann der JIT-Optimierer das Ergebnis nicht zwischenspeichern (mit Ausnahme von trivialen Fällen), und er wird schneller, wenn Sie das Ergebnis einer Eigenschaft mit langer Laufzeit zwischenspeichern. Schauen Sie sich dieses Beispiel an: why foreach is faster than for loop while reading richtextbox lines.

jedoch für einige spezielle Fälle wie:

for (int i = 0; i < myArray.Length; ++i) 

wo myArray ein Array-Objekt ist, ist der Compiler in der Lage, das Muster zu erkennen und den Code und lassen Sie die gebundenen Kontrollen zu optimieren. Es könnte langsamer sein, wenn Sie das Ergebnis der Length Eigenschaft wie Cache:

int len = myArray.Length; 
for (int i = 0; i < myArray.Length; ++i) 
+0

Angenommen, dass dies kompiliert wird, wird das o.Field in beiden Fällen vom Typ string sein. Ich nehme auch an, dass der Wert für o.Field für jedes Objekt in der Auflistung auf einen sinnvollen Wert gesetzt ist. Vielleicht verstehe ich nicht ganz, was du meinst; Könntest du genauer sein? –

+0

Überprüfen Sie den Link in meiner aktualisierten Antwort. Es ist ein spezifisches Beispiel, bei dem es einen wesentlichen Unterschied macht, wenn Sie den Rückgabewert zwischenspeichern. –

2

den Wert in einem Feld Speichern ist die schnellere Option.

Obwohl ein Methodenaufruf keinen großen Aufwand verursacht, ist es bei weitem wichtiger, den Wert einmal auf einer lokalen Variablen im Stapel zu speichern und dann abzurufen.

Ich für einen mache es konsequent.

2

Im Allgemeinen ist die zweite schneller, da die erste die Eigenschaft bei jeder Iteration neu berechnet. Hier ist ein Beispiel für etwas, die erhebliche Menge an Zeit in Anspruch nehmen könnte: Es hängt wirklich von der Umsetzung

var d = new DriveInfo("C:"); 
d.VolumeLabel; // will fetch drive label on each call 
4

. In den meisten Fällen wird (in der üblichen Praxis/Höflichkeit) davon ausgegangen, dass eine Immobilie billig ist. Es könnte jedoch sein, dass jede "get" eine nicht zwischengespeicherte Suche über eine entfernte Ressource ausführt. Für standardmäßige, einfache Eigenschaften werden Sie nie einen wirklichen Unterschied zwischen den beiden bemerken. Im schlimmsten Fall wird das Holen, Speichern und Wiederverwenden viel schneller sein.

Ich wäre versucht, get zweimal zu verwenden, bis ich weiß, dass es ein Problem gibt ... "vorzeitige Optimierung", etc ... Aber; wenn ich es in einer engen Schleife verwendete, dann Ich könnte es in einer Variablen speichern. Außer Length auf einem Array, das spezielle JIT-Behandlung hat; -p

+0

@Marc: Ist das Problem mit dem ersten Code-Snippet nicht, dass 'o.Field' tatsächlich den Wert zwischen dem Testen gegen" etwas "und dem Hinzufügen zur" Liste "ändern könnte? 'o.Field ==" something "' könnte wahr sein, aber wenn Sie 'strings.Add' aufrufen, fügen Sie" etwas anderes "hinzu? –

+0

@Grant - oh absolut könnte es, aber wieder wäre es ... nicht-Standard - oder zumindest sollte gut dokumentiert werden. Wenn es wegen Threading ist, dann haben wir natürlich nur selbst Schuld. –

+0

@Marc: Ich sage nicht, es ist eine vorzeitige Optimierung, vor allem, wenn Sie dumme Nicht-O (1) -Eigenschaften (eine Menge von ihnen in WinForms existieren) wie die, die ich in meiner Antwort verknüpft. In Multithread-Szenarios möchten Sie möglicherweise die Ergebnisse aus Gründen der Korrektheit beibehalten. –

Verwandte Themen