Ich erstelle eine Desktop-Anwendung mit einer rechenintensiven Operation, die möglicherweise mehrere Sekunden lang ausgeführt wird. Offensichtlich ist es notwendig, die Zeit dieser Operation zu minimieren. Die Operation lässt sich leicht parallelisieren (einzelne Teilaufgaben), und jede Teilaufgabe benötigt ungefähr 50 ms für einen einzelnen Thread. Bei mehreren Threads dauert jede Teilaufgabe 4-5 mal so lange, da 40-50% Zeit in GC verbraucht werden, wodurch die Beschleunigung vollständig abgebrochen wird.
Also muss ich dem GC weniger Arbeit geben. Mein erster Gedanke war zu versuchen, herauszufinden, welche Art von Objekt wurde am meisten Müll gesammelt, aber ich erkannte, dass, obwohl ich häufig Speicher Profiling, ich hatte nie nach einem Muster wie folgt gesucht. In der Regel werden die Snapshots des Heapspeichers oder die Unterschiede zwischen den Heap-Snapshots betrachtet. Diese zeigen jedoch aktive Objekte und nicht die Objekte, die zwischen diesen Snapshots erstellt und zwischen ihnen angeordnet wurden. Das ist meine erste Frage: Was ist der einfachste Weg zu finden, welche Arten erstellt und am meisten Müll gesammelt werden? Ich habe versucht, nach Methodenaufrufen zu suchen, um festzustellen, ob ein Konstruktor oft verdächtig aufgerufen wurde, aber alle Objekte, die in Millionen erstellt wurden, waren nur kleine Strukturtypen. Diese sollten keine Auswirkungen auf GC haben, selbst wenn sie in Boxen sind, wenn ich die Dinge richtig verstehe?
Der Algorithmus erstellt Hunderttausende einzelner Ergebnispunktobjekte. Diese sollen natürlich nicht gc'd sein, da sie die Ausgabe der Operation darstellen. Aber es führt mich zu meiner zweiten Frage: Ist die Zeit in der GC hauptsächlich von der Gesamtzahl der Objekte abhängig oder hängt es hauptsächlich von der Anzahl der tatsächlich gesammelten Objekte ab? Sollte ich versuchen, die Anzahl der Ergebnisobjekte zu begrenzen und stattdessen weniger, aber größere Ergebnisobjekte zu verwenden?
Bearbeiten: Ich habe die Zeit in GC mit dem VS 2010 Concurrency Visualizer gefunden. Auch in dem parallelen Teil des Codes warteten die meisten Abschnitte blockierter Threads auf gc
Bearbeiten: Ich sollte klarstellen, dass das Leistungsproblem darin besteht, dass die Ausführung auf dem Workstation-GC effektiv serialisiert wird. Sehen Sie sich beispielsweise das in diesem Beitrag beschriebene Leistungsproblem an.
Ich kann nichts dagegen tun, dass der Garbage Collector meine Threads blockiert (und ich glaube nicht, dass ich den Server-GC für eine Desktop-App haben möchte, richtig?). Um also eine lineare Beschleunigung für diese Operation zu erhalten, muss ich die Anzahl der Aufrufe des GC reduzieren. Die meiste Zeit, die verschwendet wird, wird tatsächlich von anderen Threads verschwendet, die darauf warten, dass ein Thread GC ausführt.
Wenn Ihre Aufgaben nur 50 ms benötigen, wird der Aufwand für die Erstellung von Threads mehr Zeit in Anspruch nehmen als Ihre eigentlichen Jobs. Sie können also möglicherweise nicht zu weit hinein kommen.
Um zu sehen, was da draußen ist, sind die besten Werkzeuge, die ich verwendet habe, ANTS Profiler (Speicher und Leistung). Von dort können Sie Objekte im Speicher sehen, und Unterschiede zwischen den Zeitpunkten sowie "Anzahl der Ausführungen", die Sie bekommen sollen, was Sie wollen.
Vielleicht sollten Sie versuchen, die Cachetreffer zwischen Ihren Objekten zu erhöhen.
Anstatt neue Strukturpunkte zu erstellen und dann Berechnungen in Listen / Aufzählungen durchzuführen, haben Sie versucht, eine feste Anordnung von Punkten zuzuordnen und dann die Punkte kontinuierlich zu verwenden. Auf diese Weise ordnen Sie die Objekte nur einmal zu, führen Ihre Berechnungen durch und kehren dann zurück. Sie profitieren vom Hot-Cache und Sie werden keinen GC erleiden, wenn Sie das Array vollständig wiederverwenden können.
Alte Frage, aber für diejenigen, die darüber stolpern ...
Ich hatte genau das gleiche Problem und habe es dauerhaft behoben, indem ich die Garbage-Collection im Server-Modus gesetzt habe Ссылка .
In app.config
hinzufügen:
Das hat meinen Code bereits um eine Größenordnung beschleunigt, ohne Nebenwirkungen, die ich finden konnte.
Wenn Sie genau wissen, wo Sie viele GCs generieren, habe ich auch LowLatency Ссылка brachte meine GCs auf eine einzige Generation herunter - 1 GC:
%Vor%(Die PrepareConstrainedRegions stellt sicher, dass der Finally-Block immer ausgeführt wird, aber ich bin mir nicht ganz sicher, ob das korrekt ist).
Diese Ergebnispunktobjekte. Wie im Standard-struct Point? Kann nicht von hier aus sagen, aber haben Sie versucht, den Platz für sie vorzuordnen. Die meisten deiner GC-Calls könnten ihnen Speicher zuweisen, das ist eine Menge Aufwand, sie in größeren Blöcken zu erledigen, oder sogar in einem Rutsch, wenn der Betrag berechnet werden kann, sollte es einen Schub geben.
Eine weitere Möglichkeit besteht darin, in unsicheren Code zu wechseln, vorausgesetzt, Sie können diese Berechtigung auf der Workstation erhalten. Ich weiß nicht, wie du deine Punkte verteilt hast, aber es könnte eine Zukunft sein, wenn du nur einen Speicherblock zuweist und dann mit der Zeigerarithmetik durchwirfst.
Tags und Links .net garbage-collection performance