2010-11-03 2 views
7

Während einer aktuellen Codeüberprüfung schlug ein Kollege vor, dass für Klasse mit 4 Int-Eigenschaften im Konstruktor jede zu Null führen würde zu einer Leistungseinbuße führen.Zuweisen von Ganzzahlen Feldern/Eigenschaften in einem Konstruktor

Zum Beispiel

public Example() 
    { 
     this.major = 0; 
     this.minor = 0; 
     this.revision = 0; 
     this.build = 0; 
    } 

war Sein Punkt, dass dies überflüssig ist, da sie standardmäßig eingestellt wird, wird auf Null gesetzt und Sie Overhead sind die Einführung von im Wesentlichen zweimal die gleiche Aufgabe. Mein Punkt war, dass der Leistungshit vernachlässigbar wäre, wenn überhaupt einer vorhanden wäre, und dies ist besser lesbar (es gibt mehrere Konstruktoren), da die Absicht des Zustands des Objekts nach dem Aufruf dieses Konstruktors sehr klar ist.

Was denkst du? Gibt es einen Leistungsgewinn, den es wert ist, sich hier um etwas zu kümmern?

+4

Vorsicht vor vorzeitiger (möglicher) Optimierung. – jball

+0

"Vorzeitige Optimierung ist die Wurzel allen Übels." –

+3

Wenn dies "programmers.stackexchange.com" wäre, würde ich aus Sicht der Lesbarkeit antworten. Sicher, es wird die Leistung nicht beeinträchtigen, aber es fügt dem Quellcode Rauschen hinzu. Es ist zum Beispiel, 'foreach (var item in anyEnumerable.ToList())' 'zu schreiben. –

Antwort

5

Ich glaube nicht, dass sie die gleiche Operation sind, und ist ein Leistungsunterschied.Hier ist ein-Micro es zu zeigen:

using System; 
using System.Diagnostics; 

class With 
{ 
    int x; 

    public With() 
    { 
     x = 0; 
    } 
} 


class Without 
{ 
    int x; 

    public Without() 
    { 
    } 
} 


class Test 
{ 
    static void Main(string[] args) 
    { 
     int iterations = int.Parse(args[0]); 
     Stopwatch sw = Stopwatch.StartNew(); 
     if (args[1] == "with") 
     { 
      for (int i = 0; i < iterations; i++) 
      { 
       new With(); 
      } 
     } 
     else 
     { 
      for (int i = 0; i < iterations; i++) 
      { 
       new Without(); 
      } 
     } 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
    } 
} 

Ergebnisse:

c:\Users\Jon\Test>test 1000000000 with 
8427 

c:\Users\Jon\Test>test 1000000000 without 
881 

c:\Users\Jon\Test>test 1000000000 with 
7568 

c:\Users\Jon\Test>test 1000000000 without 
819 

Nun würde das machen mir den Code ändern? Absolut nicht. Schreiben Sie zuerst den lesenswertesten Code. Wenn mit der Zuweisung besser lesbar ist, behalten Sie die Zuweisung dort bei. Obwohl ein Microbenchmark zeigt, dass es eine Kosten hat, ist das immer noch ein kleiner Kostenfaktor im Zusammenhang mit der Durchführung einer echten Arbeit. Obwohl die proportionale Differenz hoch ist, erzeugt es immer noch eine Milliarden Instanzen in 8 Sekunden in der "langsamen" Route. Meine Vermutung ist, dass es tatsächlich eine Art Optimierung für vollständig leere Konstruktoren gibt, die direkt an den vollständig leeren object() Konstruktor verkettet sind. Der Unterschied zwischen der Zuordnung zu zwei Felder und nur Zuweisen ein Feld ist viel kleiner.

In Bezug auf während der Compiler es nicht optimieren kann, bedenken Sie, dass ein Basiskonstruktor den Wert durch Reflektion oder vielleicht einen virtuellen Methodenaufruf ändern könnte. Der Compiler könnte möglicherweise bemerken, aber es scheint eine seltsame Optimierung.

+0

Danke Jon, immer schätzen Bank Tests etc. in einer Antwort. –

6

Nein, gibt es nicht. Der Compiler wird diese Operationen optimieren; Die gleiche Aufgabe wird nicht zweimal ausgeführt. Dein Kollege hat Unrecht.

[Bearbeiten basierend auf Eingabe von dem stets ausgezeichneten Jon Skeet]

Der Compiler der Operationen optimieren sollte, aber anscheinend sind sie nicht vollständig optimiert werden; Der Optimierungsgewinn ist jedoch völlig vernachlässigbar, und der Vorteil einer so expliziten Zuweisung ist gut. Ihre Kollegin mag zwar nicht völlig falsch liegen, aber sie konzentrieren sich auf eine völlig triviale Optimierung.

+7

Welcher Compiler wird Ihrer Meinung nach optimiert? Der C# -Compiler funktioniert nicht, soweit ich das beurteilen kann. –

+1

Ich glaube nicht, dass sie die gleiche Operation sind. Eine ist eine Speicherbereinigung, die andere eine explizite Zuweisung. Ich sehe keinen Grund anzunehmen, dass das entfernt würde. –

+0

Sie vermissen hier die Optimierung der JIT/Runtime. Ich bin mir ziemlich sicher, dass das weg optimiert ist. [Bearbeiten:] Könnte nur einige Laufzeiten optimieren es weg. – Dykam

0

Ich denke nicht, dass Sie sich um Performance-Hit kümmern sollten, in der Regel gibt es viele andere Orte, an denen das Programm optimiert werden kann. Auf der anderen Seite sehe ich keinen Gewinn durch die Angabe dieser Werte im Konstruktor, da sie sowieso auf 0 gesetzt werden.

3

Sie sollten sich auf Code Klarheit konzentrieren, das ist das Wichtigste. Wenn die Leistung zu einem Problem wird, messen Sie die Leistung, und sehen Sie, was Ihre Engpässe sind, und verbessern Sie diese. Es lohnt sich nicht, so viel Zeit mit der Sorge um die Leistung zu verbringen, wenn es leichter ist, Code zu verstehen.

4

Mein Verständnis ist, dass Objekte Speicher auf Null mit einem einfachen und sehr schnellen Speicher löschen gelöscht wird. Diese expliziten Zuweisungen erfordern jedoch zusätzliche IL. In der Tat, einige Werkzeuge erkennen Sie den Standardwert (in einem Feld Initialisierer) zuweisen und davon abraten.

Also würde ich sagen: tun Sie das nicht - es ist möglicherweise marginal langsamer. Aber nicht viel. Kurz gesagt, ich denke, dein Freund hat Recht.

Leider bin ich gerade auf einem mobilen Gerät, ohne die richtigen Tools, um es zu beweisen.

1

Sie können sie als Felder initialisieren direkt:

public int number = 0; 

Und ist auch klar.

+0

Und ist auch vom Compiler weg optimiert. – McKay

+1

@McKay: Beweise dafür? –

+1

Soweit ich weiß, wird NICHT vom Compiler optimiert, aber die Leistungseinbußen sind so niedrig, dass es sich nicht einmal lohnt, sie als "Strafe" zu bezeichnen. –

1

Die wichtigere Frage ist: Gibt es wirklich eine Lesbarkeitsgewinn? Wenn die Personen, die den Code verwalten, bereits wissen, dass Ints null zugewiesen sind, ist dies nur ein weiterer Code, den sie analysieren müssen. Vielleicht wäre der Code sauberer ohne Linien, die nichts tun.

1

In der Tat würde ich die Zuweisung in Konstruktor, nur für die Lesbarkeit, und zum Markieren der "Ich habe nicht vergessen, diese zu initialisieren" Absicht. Sich auf Standardverhalten zu verlassen, tendiert dazu, andere Entwickler zu verwirren.

Verwandte Themen