Heim  >  Artikel  >  Backend-Entwicklung  >  Praktisches Tutorial zum Schreiben von Hochleistungs-.NET

Praktisches Tutorial zum Schreiben von Hochleistungs-.NET

零下一度
零下一度Original
2017-06-25 09:08:431524Durchsuche

Zuordnungsrate reduzieren

Dies bedarf fast keiner Erklärung. Es reduziert die Speichernutzung, was natürlich den Druck beim GC-Recycling verringert und gleichzeitig die Speicherfragmentierung und die CPU-Auslastung verringert. Es gibt Möglichkeiten, dies zu erreichen, es kann jedoch zu Konflikten mit anderen Designs kommen.

Sie müssen jedes Objekt beim Entwerfen sorgfältig prüfen und sich fragen:

  1. Brauche ich dieses Objekt wirklich?

  2. Ist dieses Feld das, was ich brauche?

  3. Kann ich die Größe des Arrays reduzieren?

  4. Kann ich die Größe von Grundelementen reduzieren (Int64 durch Int32 ersetzen usw.)?

  5. Werden diese Objekte nur in seltenen Fällen oder nur während der Initialisierung verwendet?

  6. Ist es möglich, einige Klassen in Strukturen umzuwandeln, sodass sie auf dem Stapel zugewiesen oder Teil eines Objekts werden können?

  7. Ordne ich viel Speicher zu, nutze aber nur einen winzigen Bruchteil davon?

  8. Kann ich relevante Daten von anderen Orten erhalten?

Kleine Geschichte: In einer Funktion auf dem Server, die auf Anfragen reagiert, haben wir festgestellt, dass in einer Anfrage etwas Speicher zugewiesen wird, der größer als das Speichersegment ist. Dies führt dazu, dass wir für jede Anfrage einen vollständigen GC auslösen. Dies liegt daran, dass die CLR erfordert, dass sich alle Objekte der Generation 0 in einem Speichersegment befinden. Wenn das aktuell zugewiesene Speichersegment voll ist, wird ein neues Speichersegment geöffnet Segment wird gleichzeitig geöffnet. Das Speichersegment wird für 2 Generationen recycelt. Dies ist keine gute Implementierung, da wir keine andere Möglichkeit haben, als die Speicherzuweisung zu reduzieren.

Die wichtigste Regel

Es gibt eine Grundregel für Hochleistungsprogrammierung mit Garbage Collection, die eigentlich eine Leitregel für das Codedesign ist.

Die zu sammelnden Objekte sind entweder in der Generation 0 oder gar nicht.
Sammeln Sie Objekte in der Generation 0 oder überhaupt nicht Das Objekt muss eine extrem kurze Lebensdauer haben und darf während der GC niemals berührt werden. Wenn dies nicht möglich ist, sollten sie so schnell wie möglich auf zwei Generationen übertragen werden und dort für immer bleiben. Sie werden niemals recycelt. Dies bedeutet, dass Sie einen Verweis auf ein langlebiges Objekt für immer behalten. Normalerweise bedeutet dies auch, dass Objekte wiederverwendet werden können, insbesondere Objekte in großen Objekthaufen. Das GC-Recycling für jede höhere Generation wird zeitaufwändiger sein als für die vorherige Generation. Wenn Sie viele Objekte der Generation 0,1 und wenige Objekte der Generation 2 behalten möchten. Selbst wenn der Hintergrund-GC für die Wiederverwertung über zwei Generationen aktiviert ist, verbraucht er viele CPU-Vorgänge. Möglicherweise möchten Sie diesen Teil der CPU lieber für die Anwendung als für GC verwenden.


Hinweis Sie haben vielleicht schon einmal ein Sprichwort gehört, dass jedes 10. Generation-0-Recycling zu einem 1-Generation-Recycling führt und jedes 10. Generation-1-Recycling zu einem 2-Generation-Recycling führt. Das ist eigentlich falsch, aber Sie müssen verstehen, dass Sie so viele schnelle Sammlungen der Generation 0 wie möglich und eine kleine Anzahl von Sammlungen der Generation 2 generieren möchten.

Sie sollten das Recycling der Generation 1 besser vermeiden, vor allem, weil Objekte, die von Generation 0 auf Generation 1 befördert wurden, zu diesem Zeitpunkt auf Generation 2 übertragen werden. Generation 1 ist ein Puffer für Objekte, die in Generation 2 eintreten.

Idealerweise sollte jedes Objekt, das Sie zuweisen, seinen Lebenszyklus vor der nächsten Sammlung der Generation 0 beenden. Sie können die Zeit zwischen GCs messen und sie mit der Lebensdauer von Objekten in Ihrer Anwendung vergleichen. Informationen zum Einsatz von Tools zur Messung von Lebenszyklen finden Sie am Ende dieses Kapitels.
Sie sind es vielleicht nicht gewohnt, so zu denken, aber diese Regel betrifft jeden Aspekt der Bewerbung. Sie müssen häufig darüber nachdenken und Ihre Mentalität grundlegend ändern, um diese wichtigste Regel umzusetzen.


Verkürzen Sie den Lebenszyklus von Objekten

Je kürzer der Umfang eines Objekts, desto geringer ist die Chance, dass es beim nächsten GC in die nächste Generation befördert wird. Generell gilt: Erstellen Sie Objekte erst, wenn Sie sie benötigen.

Gleichzeitig können Ausnahmen früher erstellt werden, wenn die Kosten für die Objekterstellung so hoch sind, sodass sie andere Verarbeitungslogiken nicht beeinträchtigen.

Darüber hinaus müssen Sie sicherstellen, dass das Objekt den Geltungsbereich so früh wie möglich verlässt. Bei lokalen Variablen können Sie deren Lebensdauer nach der letzten Verwendung oder sogar vor Ende der Methode beenden. Sie können den Code mit {} einschließen, was sich nicht auf Ihre Ausführung auswirkt, aber der Compiler geht davon aus, dass das Objekt in diesem Bereich seinen Lebenszyklus abgeschlossen hat und nicht mehr verwendet wird. Wenn Sie die Methode eines Objekts aufrufen müssen, versuchen Sie, das Zeitintervall zwischen dem ersten und dem letzten Aufruf zu verkürzen, damit der GC das Objekt so früh wie möglich wiederverwenden kann.

Wenn das Objekt mit einigen Objekten verknüpft (referenziert) ist, die über einen längeren Zeitraum beibehalten werden, müssen Sie deren Referenzbeziehungen freigeben. Möglicherweise verfügen Sie über mehr Nullprüfungen, wodurch der Code möglicherweise komplexer wird. Es kann auch zu einem Spannungsverhältnis zwischen dem verfügbaren Status des Objekts (immer der vollständige Status verfügbar) und der Effizienz führen, insbesondere beim Debuggen.
Eine Lösung besteht darin, das zu löschende Objekt auf andere Weise umzuwandeln, beispielsweise in eine Protokollnachricht, damit die relevanten Informationen beim anschließenden Debuggen abgefragt werden können.
Eine andere Methode besteht darin, dem Code konfigurierbare Optionen hinzuzufügen (ohne die Beziehung zwischen Objekten freizugeben): Beim Ausführen des Programms (oder beim Ausführen eines bestimmten Teils des Programms, z. B. einer bestimmten Anforderung) werden die Objekte in diesem Modus nicht freigegeben Referenzbeziehung, sondern um das Objekt für das Debuggen so praktisch wie möglich zu halten.

Reduzieren Sie die Tiefe der Objekthierarchie

Wie zu Beginn dieses Kapitels erwähnt, durchläuft der GC beim Recycling die Referenzbeziehung des Objekts. Im Server-GC-Modus wird der GC im Multithread-Modus ausgeführt. Wenn ein Thread jedoch ein Objekt auf einer tiefen Ebene verarbeiten muss, müssen alle Threads, die es verarbeitet haben, warten, bis dieser Thread die Verarbeitung abgeschlossen hat, bevor sie beendet werden . In zukünftigen CLR-Versionen müssen Sie diesem Problem nicht allzu viel Aufmerksamkeit schenken. GC wird einen besseren Markierungsalgorithmus für den Lastausgleich während der Multithread-Ausführung verwenden. Wenn Ihre Objektebene jedoch sehr tief ist, müssen Sie diesem Problem dennoch Aufmerksamkeit schenken.

Referenzen zwischen Objekten reduzieren

Dies hängt mit der Tiefe des vorherigen Abschnitts zusammen, aber es gibt auch einige andere Faktoren.
Wenn ein Objekt auf viele Objekte (Array, Liste) verweist, wird das Durchlaufen der Objekte viel Zeit in Anspruch nehmen. Es ist der GC, der lange Zeit ein Problem verursacht, da er über ein komplexes Beziehungsdiagramm verfügt.
Ein weiteres Problem besteht darin, dass Sie den Lebenszyklus des Objekts nicht genau vorhersagen können, wenn Sie nicht einfach bestimmen können, wie viele Referenzbeziehungen ein Objekt hat. Diese Komplexität zu reduzieren ist durchaus notwendig, nicht nur um den Code robuster zu machen, sondern auch um das Debuggen zu erleichtern und eine bessere Leistung zu erzielen.
Beachten Sie außerdem, dass Referenzen zwischen Objekten verschiedener Generationen ebenfalls zu GC-Ineffizienz führen, insbesondere Referenzen von alten Objekten auf neue Objekte. Wenn beispielsweise ein Objekt der 2. Generation eine Referenzbeziehung in einem Objekt der 0. Generation hat, müssen jedes Mal, wenn ein GC der Generation 0 auftritt, auch einige der Objekte der 2. Generation gescannt werden, um festzustellen, ob sie noch Verweise auf das Objekt der 0. Generation enthalten . Auch wenn dies kein vollständiger GC ist, ist es dennoch unnötige Arbeit und Sie sollten versuchen, diese Situation zu vermeiden.

Vermeiden Sie das Anheften von Objekten (Anheften)

Das Anheften von Objekten kann die Sicherheit der Daten gewährleisten, die vom verwalteten Code an den nativen Code übergeben werden. Üblich sind Arrays und Strings. Wenn Ihr Code nicht mit nativem Code interagieren muss, müssen Sie sich über den Leistungsaufwand keine Sorgen machen.
Das Anheften eines Objekts bedeutet, dass das Objekt während der Speicherbereinigung (Komprimierungsphase) nicht verschoben werden kann. Obwohl das Anheften von Objekten keinen großen Overhead verursacht, behindert es den GC-Recyclingvorgang und erhöht die Möglichkeit einer Speicherfragmentierung. Der GC zeichnet die Position von Objekten während des Recyclings auf, sodass der Raum zwischen ihnen bei der Neuzuweisung genutzt werden kann. Wenn jedoch viele angeheftete Objekte vorhanden sind, führt dies zu einer erhöhten Speicherfragmentierung.
Peg kann explizit oder implizit sein. Was gezeigt wird, ist, es mit dem Parameter GCHandleType.Pinned festzulegen, wenn GCHandle verwendet wird, oder das Schlüsselwort „fixed“ im unsicheren Modus zu verwenden. Der Unterschied zwischen der Verwendung des festen Schlüsselworts und GCHandle besteht darin, ob die Dispose-Methode explizit aufgerufen wird. Obwohl die Verwendung von „fixed“ sehr praktisch ist, kann sie in asynchronen Situationen nicht verwendet werden. Sie können jedoch dennoch ein Handle-Objekt (GCHandle) erstellen und es während des Rückrufs zurückgeben und verarbeiten.
Implizites Anheften kommt häufiger vor, ist aber schwerer zu erkennen und zu entfernen. Das offensichtlichste Beispiel ist die Übergabe von Objekten an nicht verwalteten Code über Plattformaufrufe (P/Invoke). Es geht nicht nur um Ihren Code – einige der verwalteten APIs, die Sie häufig aufrufen, rufen tatsächlich auch nativen Code auf und pinnen Objekte an.
Die CLR wird auch einige seiner eigenen Daten festlegen, aber dies ist normalerweise etwas, das Sie sich nicht interessieren.
Idealerweise sollten Sie versuchen, Objekte nicht so weit wie möglich zu stecken. Wenn dies nicht möglich ist, befolgen Sie die vorherigen wichtigen Regeln und versuchen Sie, diese festgehaltenen Objekte so bald wie möglich zu veröffentlichen. Wenn das Objekt einfach angeheftet und freigegeben wird, besteht kaum eine große Chance, dass es den Erfassungsvorgang beeinträchtigt. Sie möchten auch vermeiden, dass mehrere Objekte gleichzeitig festgehalten werden. Es ist etwas besser, wenn festgestellte Objekte gegen die 2. Generation ausgetauscht oder in LOH zugewiesen werden. Gemäß dieser Regel können Sie dem großen Objektheap einen großen Puffer zuweisen und den Puffer entsprechend dem tatsächlichen Bedarf selbst verwalten. Oder pufferen Puffer für Paar kleine Objekte zu und upgraden Sie sie dann auf die Generation 2 ein, bevor Sie sie festhalten. Dies ist besser, als das Objekt direkt an Generation 0 anzuheften.

Das obige ist der detaillierte Inhalt vonPraktisches Tutorial zum Schreiben von Hochleistungs-.NET. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn