Frage

Bevor Sie aus dem Bauch heraus reagieren, wie ich anfangs tat, die ganze Frage lesen Sie bitte. Ich weiß, sie fühlen Sie sich schmutzig, ich weiß, wir haben alle vorher verbrannt worden, und ich weiß es nicht „guter Stil“, sondern sind öffentliche Felder immer in Ordnung?

Ich arbeite an einer ziemlich großen Engineering-Anwendung Skala, und arbeitet mit einem in Speichermodell einer Struktur erzeugt (alles von Hochhaus zu überbrücken zu vergießen, spiele keine Rolle). Es gibt eine Unmenge von geometrischer Analyse und Berechnung an diesem Projekt beteiligt. Um dies zu unterstützen, wird das Modell aus vielen kleinen unveränderlichen schreibgeschützt structs zusammengesetzt Dinge wie Punkte darstellen, Liniensegmente, usw. Einige der Werte dieser Strukturen (wie die Koordinaten der Punkte) zugegriffen Dutzende oder Hunderte von Millionen Zeiten während einer typischen Programmausführung. Aufgrund der Komplexität der Modelle und das Volumen der Berechnung, die Leistung ist absolut entscheidend.

Ich glaube, dass wir alles, was wir tun unsere Algorithmen zu optimieren, Performance-Test Flaschenhälse, um zu bestimmen, die richtigen Datenstrukturen verwenden, etc. etc. Ich glaube nicht, dies ein Fall der vorzeitigen Optimierung ist. Performance-Tests zeigt Größenordnung (mindestens) steigert die Leistung, wenn Felder direkt und nicht durch eine Eigenschaft auf dem Objekt zugreifen. Mit dieser Information, und die Tatsache, dass wir auch die gleichen Informationen wie Eigenschaften aussetzen können zur Unterstützung der Datenbindung und andere Situationen, ... ist das OK? Denken Sie daran, lesen Sie nur die Felder auf unveränderliche Strukturen. Kann jemand denken Sie an einen Grund, dies bereuen werde ich?

Hier ist ein Beispieltest App:


struct Point {
    public Point(double x, double y, double z) {
        _x = x;
        _y = y;
        _z = z;
    }

    public readonly double _x;
    public readonly double _y;
    public readonly double _z;

    public double X { get { return _x; } }
    public double Y { get { return _y; } }
    public double Z { get { return _z; } }
}

class Program {
    static void Main(string[] args) {
        const int loopCount = 10000000;

        var point = new Point(12.0, 123.5, 0.123);

        var sw = new Stopwatch();
        double x, y, z;
        double calculatedValue;
        sw.Start();
        for (int i = 0; i < loopCount; i++) {
            x = point._x;
            y = point._y;
            z = point._z;
            calculatedValue = point._x * point._y / point._z;
        }
        sw.Stop();
        double fieldTime = sw.ElapsedMilliseconds;
        Console.WriteLine("Direct field access: " + fieldTime);

        sw.Reset();
        sw.Start();
        for (int i = 0; i < loopCount; i++) {
            x = point.X;
            y = point.Y;
            z = point.Z;
            calculatedValue = point.X * point.Y / point.Z;
        }
        sw.Stop();
        double propertyTime = sw.ElapsedMilliseconds;
        Console.WriteLine("Property access: " + propertyTime);

        double totalDiff = propertyTime - fieldTime;
        Console.WriteLine("Total difference: " + totalDiff);
        double averageDiff = totalDiff / loopCount;
        Console.WriteLine("Average difference: " + averageDiff);

        Console.ReadLine();
    }
}

Ergebnis:
Direkte Feld Zugang: 3262
Zugang zur Immobilie: 24248
Gesamtdifferenz: 20986
Durchschnittliche Differenz: 0.00020986


Es ist nur 21 Sekunden, aber warum nicht?

War es hilfreich?

Lösung

Ihr Test ist nicht wirklich fair zu den Immobilien-basierten Versionen. Der JIT ist intelligent genug, um einfache Eigenschaften inline, so dass sie eine Laufzeitleistung äquivalent zu dem direkten Feld Zugangs haben, aber es ist nicht klug genug (heute) scheint zu erkennen, wenn die Eigenschaften zugreifen konstante Werte.

In Ihrem Beispiel der gesamte Schleife Körper der Feld Zugriff Version ist optimiert weg, immer nur:

for (int i = 0; i < loopCount; i++)
00000025  xor         eax,eax 
00000027  inc         eax  
00000028  cmp         eax,989680h 
0000002d  jl          00000027 
}

, während der zweiten Version, ist eigentlich die Gleitkommadivision bei jeder Iteration durchführt:

for (int i = 0; i < loopCount; i++)
00000094  xor         eax,eax 
00000096  fld         dword ptr ds:[01300210h] 
0000009c  fdiv        qword ptr ds:[01300218h] 
000000a2  fstp        st(0) 
000000a4  inc         eax  
000000a5  cmp         eax,989680h 
000000aa  jl          00000096 
}

machen nur zwei kleine Änderungen an Ihrer Anwendung, um es realistischer die beiden Operationen praktisch identisch in Leistung macht.

Zuerst Randomisierung der Eingabewert, so dass sie keine Konstanten sind und die JIT ist nicht intelligent genug, die Spaltung vollständig zu entfernen.

Ändern von:

Point point = new Point(12.0, 123.5, 0.123);

zu:

Random r = new Random();
Point point = new Point(r.NextDouble(), r.NextDouble(), r.NextDouble());

Zweitens, stellen Sie sicher, dass die Ergebnisse der einzelnen Schleifeniterationslatenzzeit irgendwo verwendet:

Vor jeder Schleife gesetzt calculatedValue = 0, so sie beide an der gleichen Stelle beginnen. Nach jeder Schleife Aufruf Console.WriteLine (calculatedValue.ToString ()), um sicherzustellen, dass das Ergebnis „verwendet“, so der Compiler es nicht weg zu optimieren. Schließlich den Körper der Schleife ändern von "calculatedValue = ..." bis "calculatedValue + = ...", so dass jede Iteration verwendet wird.

Auf meinem Rechner dieser Veränderungen (mit einer Release-Build) ergeben die folgenden Ergebnisse:

Direct field access: 133
Property access: 133
Total difference: 0
Average difference: 0

So wie wir erwarten, die x86 für jede dieser modifizierten Schleifen identisch ist (mit Ausnahme der Schleifenadresse)

000000dd  xor         eax,eax 
000000df  fld         qword ptr [esp+20h] 
000000e3  fmul        qword ptr [esp+28h] 
000000e7  fdiv        qword ptr [esp+30h] 
000000eb  fstp        st(0) 
000000ed  inc         eax  
000000ee  cmp         eax,989680h 
000000f3  jl          000000DF (This loop address is the only difference) 

Andere Tipps

Da Sie mit unveränderlichen Objekten mit Nur-Lese-Feldern beschäftigen, würde ich sagen, dass man den einen Fall getroffen hat, wenn ich eine schmutzige Gewohnheit nicht Öffentliche Bereiche zu finden sein.

IMO, ist die „keine öffentlichen Felder“ Regel eine dieser Regeln, die technisch korrekt sind, aber wenn Sie eine Bibliothek entwerfen soll von der Öffentlichkeit genutzt werden, ist es unwahrscheinlich, Sie Probleme verursachen, wenn Sie es brechen.

Bevor ich zu massiv erhalten Downvoted, sollte ich hinzufügen, dass Kapselung ist eine gute Sache. In Anbetracht der Invarianten „muss die Value-Eigenschaft null sein, wenn HasValue falsch ist“, dieser Entwurf ist fehlerhaft:

class A {
    public bool HasValue;
    public object Value;
}

Da jedoch unveränderlich, dieser Entwurf ebenso fehlerhaft ist:

class A {
    public bool HasValue { get; set; }
    public object Value { get; set; }
}

Das richtige Design ist

class A {
    public bool HasValue { get; private set; }
    public object Value { get; private set; }

    public void SetValue(bool hasValue, object value) {
        if (!hasValue && value != null)
            throw new ArgumentException();
        this.HasValue = hasValue;
        this.Value    = value;
    }
}

(und noch besser wäre es, eine Initialisierung Konstruktor zur Verfügung zu stellen und die Klasse unveränderlich machen).

Ich weiß, dass Sie etwas schmutzig fühlen dies zu tun, aber es ist nicht ungewöhnlich, Regeln und Richtlinien Schuss in der Hölle zu kommen, wenn die Leistung ein Problem wird. Zum Beispiel, ganz wenige stark frequentierte Websites mit MySQL haben Datenduplizierung und denormalized Tabellen. Andere geht noch verrückten .

Die Moral der Geschichte - es gegen alles gehen kann man gelehrt wurden oder geraten, aber die Benchmarks liegen nicht. Wenn es funktioniert besser, tun Sie es einfach.

Wenn Sie wirklich brauchen, dass zusätzliche Leistung, dann ist es wahrscheinlich das Richtige zu tun. Wenn Sie nicht die zusätzliche Leistung benötigen, dann ist es wahrscheinlich nicht.

Rico Mariani hat ein paar related posts:

Ich persönlich die einzige Zeit ist in einer sehr umsetzungsspezifischen privaten geschachtelte Klasse öffentliche Felder in Betracht ziehen würde verwendet wird.

Andere Zeiten es fühlt sich einfach zu „falsch“, es zu tun.

Die CLR kümmert sich um die Leistung nehmen, indem Sie die Methode / Eigenschaft Optimierung aus (in Release-Builds), so dass sollte kein Problem sein.

Nicht, dass ich mit den anderen Antworten nicht einverstanden ist, oder mit Ihrem Abschluss ... aber ich würde gerne wissen, wo Sie die Größenordnung Leistungsdifferenz stat aus zu bekommen. Als ich den C # -Compiler verstehen, jeder einfache Eigenschaft (ohne zusätzlichen Code nicht aus direkten Zugriff auf das Feld), sollte durch den JIT-Compiler als ein direkter Zugriff sowieso inlined erhalten.

Die AdvantEdge der Verwendung von Eigenschaften auch in diesen einfachen Fällen (in den meisten Fällen) war, dass es als eine Eigenschaft durch Schreiben Sie für zukünftige Änderungen ermöglichen, die die Eigenschaft ändern könnten. (Obwohl in Ihrem Fall gäbe es keine derartige Veränderungen in Zukunft natürlich)

Versuchen Sie, ein Release-Build kompiliert und direkt von der exe statt durch den Debugger ausgeführt wird. Wenn die Anwendung über einen Debugger ausgeführt wurde, dann wird der JIT-Compiler die Eigenschaftenaccessoren nicht inline. Ich war nicht in der Lage, Ihre Ergebnisse zu replizieren. In der Tat, jeder Test lief ich darauf hingewiesen, dass es praktisch kein Unterschied in der Ausführungszeit war.

Aber, wie die andere, die ich nicht vollständig auf direkten Zugang zum Feld Oppossed bin. Vor allem, weil es einfach ist das Feld privaten und fügen Sie eine öffentliche Eigenschaftenaccessor zu einem späteren Zeitpunkt ohne erforderlich macht noch mehr Code-Änderungen zu machen, um die Anwendung zu kompilieren.

Edit: Okay, verwendet meine ersten Tests einen int-Datentyp statt Doppel. Ich sehe einen großen Unterschied, wenn verdoppelt werden. Mit Ints ist die direkte vs. Eigenschaft praktisch gleich. Mit Doppel-Eigenschaft Zugang über 7x langsamer als die direkten Zugriff auf meinem Rechner. Dies ist etwas verwirrend für mich.

Es ist auch wichtig, um die Tests außerhalb des Debuggers auszuführen. Auch in Version baut der Debugger fügt Overhead, die die Ergebnisse verzerrt.

Hier einige Szenarien, in denen es in Ordnung ist (aus dem Framework Design Guidelines Buch):

  
      
  • DO konstante Felder für Konstanten   das wird sich nie ändern.
  •   
  • Verwenden Sie keine öffentlichen   Nur-Lese-statische Felder für vordefinierte   Objektinstanzen.
  •   

Und wo es nicht ist:

  
      
  • assign Sie keine Instanzen von wandelbaren   Typen Felder schreibgeschützt.
  •   

Von dem, was Sie gesagt haben ich nicht bekommen, warum Ihre trivialen Eigenschaften nicht durch die JIT inlined erhalten?

Wenn Sie ändern Ihren Test die temporären Variablen zu verwenden, greifen Sie zuweisen, anstatt direkt die Eigenschaften in Ihrer Berechnung finden Sie eine große Leistungssteigerung sehen:

        sw.Start();
        for (int i = 0; i < loopCount; i++)
        {
            x = point._x;
            y = point._y;
            z = point._z;
            calculatedValue = x * y / z;
        }
        sw.Stop();
        double fieldTime = sw.ElapsedMilliseconds;
        Console.WriteLine("Direct field access: " + fieldTime);

        sw.Reset();
        sw.Start();
        for (int i = 0; i < loopCount; i++)
        {
            x = point.X;
            y = point.Y;
            z = point.Z;
            calculatedValue = x * y / z;
        }
        sw.Stop();

Vielleicht werde ich jemand anderes wiederholen, aber hier ist mein Punkt auch, wenn es helfen kann.

Lehren sind Ihnen die Werkzeuge, um Sie ein gewisses Maß an Leichtigkeit erreichen müssen, wenn solche Situationen zu begegnen.

Die Agile Software-Entwicklungsmethodik sagt, dass Sie das Produkt zu Ihrem Client, egal zuerst zu liefern, was Ihr Code aussehen könnte. Zweitens können Sie optimieren und Ihren Code „schön“ machen oder gemäß den Programmierzustände der Technik.

Hier entweder Sie oder Ihr Kunde benötigen Leistung. Innerhalb Ihres Projektes ist PERFORMANCE CRUCIAL, wenn ich das richtig verstehen.

Also, ich denke, Sie werden mir zustimmen, dass wir nicht über egal, was der Code aussehen könnte, oder ob es die „Kunst“ respektiert. Tun Sie, was Sie haben, um es performant und mächtig zu machen! Eigenschaften lassen Sie Ihren Code auf „Format“ die Daten-I / O, falls erforderlich. Eine Eigenschaft hat seine eigene Speicheradresse, dann ist es für sein Mitglied Adresse aussieht, wenn Sie das Mitglied Wert zurück, so bekam man zwei Durchsuchung Adresse. Wenn die Leistung so kritisch ist, tut es einfach, und Ihre unveränderlichen Mitglieder öffentlich machen. : -)

Dies spiegelt einige andere Blickwinkel zu, wenn ich das richtig gelesen. :)

Haben

einen guten Tag!

Typen, die Funktionalität kapseln sollte Eigenschaften verwenden. Typen, die nur dazu dienen, Daten zu halten, sollen die öffentlichen Bereiche, außer im Fall von unveränderlichen Klassen (wo Verpackung Felder in Nur-Lese-Eigenschaften sind der einzige Weg, um zuverlässig sie vor Veränderungen zu schützen) zu verwenden. Offenlegen von Mitgliedern als öffentliche Felder verkündet im Wesentlichen „diese Mitglieder können jederzeit frei, ohne Rücksicht auf irgendetwas anderes geändert werden“. Wenn die in Rede stehenden Art ein Klassentyp ist, verkündet es weiter: „Wer einen Verweis auf diese Sache ermöglicht wird entlarvt der Empfänger diese Elemente jederzeit in irgendeiner Art und Weise Gutdünken zu verändern.“ Während man nicht öffentliche Felder in Fällen, in denen eine solche Proklamation wäre unangemessen aussetzen sollte, sollte man öffentliche Felder in Fällen aussetzen, wo eine solche Verkündigung angemessen wäre und Client-Code von den getroffenen Annahmen dadurch aktiviert profitieren könnten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top