Ich hoffe, dies ist ein gültiger Beitrag hier, es ist eine Kombination aus C # -Ausgaben und Hardware.
Ich benchmarkiere unseren Server, weil wir Probleme mit der Leistung unserer quant-Bibliothek (geschrieben in C #) gefunden haben. Ich habe die gleichen Performance-Probleme mit einigen einfachen C # -Code simuliert - Durchführung sehr hoher Speicherverbrauch.
Der folgende Code ist in einer Funktion, die aus einem Threadpool hervorgeht, bis zu einem Maximum von 32 Threads (weil unser Server 4x CPUs x 8 Kerne hat).
Dies alles ist auf .Net 3.5
Das Problem ist, dass wir sehr unterschiedliche Leistungen bekommen. Ich führe die unten stehende Funktion 1000 mal aus. Die durchschnittliche Zeit, die für die Ausführung des Codes benötigt wird, kann 3,5 Sekunden betragen, aber die schnellste Zeit beträgt nur 1,2 Sekunden und die langsamste Zeit beträgt 7 Sekunden - für die exakt gleiche Funktion!
Ich habe den Speicherverbrauch gegenüber den Zeitangaben grafisch dargestellt, und es scheint keine Korrelation mit dem GC-Kicking zu geben.
Eine Sache, die ich bemerkt habe, ist, dass wenn man in einem einzigen Thread läuft, die Timings identisch sind und es keine wilde Abweichung gibt. Ich habe auch CPU-gebundene Algorithmen getestet und die Timings sind auch identisch. Das hat uns dazu gebracht, uns zu fragen, ob der Speicherbus es einfach nicht schafft.
Ich frage mich, ob das ein weiteres .net- oder C # -Problem sein könnte, oder ist es etwas mit unserer Hardware zu tun? Wäre das die gleiche Erfahrung, wenn ich C ++ oder Java benutzt hätte ?? Wir verwenden 4x Intel x7550 mit 32 GB RAM. Gibt es überhaupt eine Lösung für dieses Problem?
%Vor%(der Code soll nur die Erinnerung betonen)
Ich würde den Threadpool-Code einschließen, aber wir haben eine nicht standardmäßige Threadpool-Bibliothek verwendet.
EDIT: Ich habe "size1" auf 100000 reduziert, was im Grunde nicht viel Speicher benötigt und ich bekomme immer noch viel Jitter. Dies deutet darauf hin, dass es nicht die Menge an Speicher ist, die übertragen wird, sondern die Häufigkeit des Speicherzugriffs?
Es ist nicht genug, um weiter zu gehen, aber hier sind einige Bereiche, die Sie suchen sollten:
Sie treffen hier ziemlich grundlegende Einschränkungen der Maschine. Sie haben viele Kerne, aber es gibt immer noch nur einen Speicherbus. Wenn Ihre Threads also viele Daten mischen, werden sie wahrscheinlich durch die Bandbreite dieses einzelnen Busses gedrosselt. Das ist Amdahls Gesetz bei der Arbeit.
Es gibt eine mögliche Optimierung, die von der Art des Betriebssystems abhängt, das diese Maschine ausführt. Dies ist Server-Hardware, aber wenn Sie eine Nicht-Server-Version von Windows haben, wird der Garbage Collector im Workstation-Modus ausgeführt. Sie können dann das Element <gcServer>
in der Datei .config der Anwendung verwenden, um nach der Serverversion des Collectors zu fragen. Es verwendet mehrere Heaps, so dass die Threads nicht so oft um die GC-Heap-Sperre kämpfen, wenn sie Speicher reservieren. Ymmv.
List verwendet Arrays intern zum Speichern. Ich glaube, es wird versuchen, die Größe des Arrays jedes Mal zu verdoppeln, wenn es die Grenze des freien Speicherplatzes in der Liste erreicht.
Wenn Sie in die Schleife gehen, werden größere und größere zusammenhängende Speicherblöcke benötigt, um die neuen Arrays zuzuweisen, wenn die Liste wächst. Mit einem Thread ist das ziemlich einfach. Mit 2+ Threads konkurrieren Sie um große Teile zusammenhängender Speicher. Es würde den GC zu zufälligen Zeiten auslösen, da die Arrays größer wurden und zusammenhängender Speicher schwerer zu finden war.
An diesem Punkt scheint es, als wäre das Raten einfach eine Vermutung. Wirklich, was Sie brauchen, sind mehr Informationen.
Ich würde einen Profiler anschließen oder einige Windows-Leistungsindikatoren einrichten:
Sie sollten einige Leistungsindikatoren hinzufügen können, die auf den Prozess zentriert sind. Sie können sich ansehen, wie viele Threads hochgespielt werden, wie viel Arbeitsspeicher belegt ist. Ich würde hier einige der anderen Vorschläge berücksichtigen und das Szenario, nach dem Sie suchen, messen. Wenn Sie die Daten des Leistungsindikators in eine CSV-Datei ablegen, können Sie die Ergebnisse sogar schnell grafisch darstellen, um einige gute Daten zu erhalten, an denen Sie wirklich herumkauen können. Wenn Sie herausfinden können, welcher Messwert sich mit dem 1.2s vs 7s Szenario ändert, können Sie damit beginnen, fundierte Vermutungen darüber anzustellen, was vor sich geht, und weiter vorgehen.
Synchrone Aufrufe an gemeinsam genutzte Ressourcen, wie die Konsole oder das Dateisystem, werden die Leistung erheblich beeinträchtigen, aber nach dem Aussehen der Dinge ist dieser Code nur maximale CPU und die Zeitabweichungen müssen durch andere Prozesse verursacht werden, die CPU-Zeit anfordern.
Tags und Links memory .net c# performance memory-management