Welche ungewöhnlichen, unerwarteten Konsequenzen sind in Bezug auf Leistung, Speicher usw. aufgetreten, wenn Sie von der Ausführung Ihrer .NET-Anwendungen unter dem 64-Bit-JIT im Vergleich zum 32-Bit-JIT wechseln? Ich interessiere mich für das Gute, interessiere mich aber mehr für die überraschend schlechten Themen, denen die Leute begegnet sind.
Ich schreibe gerade eine neue .NET-Anwendung, die sowohl in 32bit als auch in 64bit eingesetzt wird. Es gab viele Fragen in Bezug auf die Probleme mit der Portierung der Anwendung - ich bin nicht besorgt über die "gotchas" von a Programmierung / Portierung . (zB: natives / COM-Interop korrekt behandeln, in Structs eingebettete Referenztypen, die die Größe der Struktur ändern usw.)
Aber diese Frage und ihre Antwort haben mich zum Nachdenken gebracht - Welche anderen Themen übersehe ich?
Es gab viele Fragen und Blogbeiträge, die sich mit diesem Thema beschäftigen oder einen Aspekt davon betreffen, aber ich habe nichts gesehen, was eine anständige Liste von Problemen zusammengestellt hat.
Insbesondere - Meine Anwendung ist sehr CPU-gebunden und hat riesige Speicherbelegungsmuster (daher die Notwendigkeit für 64 Bit an erster Stelle), sowie grafisch in der Natur. Ich mache mir Sorgen darüber, welche anderen versteckten Probleme in der CLR oder JIT unter 64-Bit-Windows (mit .NET 3.5sp1) auftreten können.
Hier sind einige Probleme, die mir derzeit bekannt sind:
Ich würde gerne wissen, welche anderen, spezifischen Probleme die Leute im JIT auf 64bit Windows entdeckt haben, und auch, wenn es irgendwelche Workarounds für die Leistung gibt.
Danke euch allen!
---- BEARBEITEN -----
Nur um zu klären -
Ich bin mir bewusst, dass der Versuch, früh zu optimieren, oft schlecht ist. Ich bin mir bewusst, dass das zweite Raten des Systems oft schlecht ist. Ich weiß auch, dass die Portabilität zu 64bit ihre eigenen Probleme hat - wir laufen und testen täglich auf 64bit-Systemen, um dabei zu helfen. usw.
Meine Anwendung ist jedoch nicht Ihre typische Geschäftsanwendung. Es ist eine wissenschaftliche Softwareanwendung. Wir haben viele Prozesse, die mit 100% CPU auf allen Kernen (es ist in hohem Grade threaded) stundenlang sitzen.
Ich habe viel Zeit damit verbracht, die Anwendung zu profilieren, und das macht einen großen Unterschied. Die meisten Profiler deaktivieren jedoch viele Funktionen des JIT, sodass die kleinen Details in Dingen wie Speicherzuweisung, Inlining im JIT usw. sehr schwer feststellbar sind, wenn Sie unter einem Profiler laufen. Daher mein Bedürfnis nach der Frage.
Ich erinnere mich, dass ich ein Problem von einem IRC-Kanal hörte, den ich häufig höre. Es optimiert die temporäre Kopie in diesem Fall:
%Vor%Setzen Sie die Racebedingung zurück und verursachen Sie mögliche Null-Referenz-Ausnahmen.
Ein besonders problematisches Leistungsproblem in .NET bezieht sich auf das schlechte JIT:
Grundsätzlich funktionieren Inlining und Strukturen nicht gut zusammen auf x64 (obwohl diese Seite suggeriert Inlining funktioniert jetzt, aber nachfolgende redunant Kopien werden nicht beseitigt, das klingt verdächtig angesichts der winzigen Perf. Unterschied) .
In jedem Fall, nachdem ich lange genug mit .NET gerungen habe, ist meine Lösung, C ++ für alles Numerische zu verwenden. Selbst in "guten" Fällen für .NET, wo Sie sich nicht mit Strukturen beschäftigen und Arrays verwenden, bei denen die Grenzenüberprüfung optimiert ist, schlägt C ++ .NET hängt ab .
Wenn Sie etwas Komplizierteres tun als Punktprodukte, wird das Bild sehr schnell schlechter; Der .NET-Code ist sowohl länger als auch weniger lesbar (weil Sie Zeug manuell einfügen müssen und / oder keine Generika verwenden können) und viel langsamer.
Ich habe auf Eigen in C ++ umgestellt: es ist absolut großartig, was zu lesbarem Code führt Hochleistung; Ein dünner C ++ / CLI-Wrapper bietet dann die Verbindung zwischen der Compute-Engine und der .NET-Welt.
Eigen funktioniert mit Template-Metaprogrammierung; kompiliert Vektorausdrücke in SSE-Instruktionen und macht eine Menge der bösartigsten cache-bezogenen Schleife, die für Sie abrollt und neu arrangiert; und obwohl es sich auf lineare Algebra konzentriert, wird es auch mit Ganzzahlen und Nicht-Matrix-Array-Ausdrücken arbeiten.
Wenn zum Beispiel P
eine Matrix ist, funktioniert diese Art von Sachen einfach:
... das keine zeitweise transponierte Variante von P zuweist und nicht das gesamte Matrixprodukt, sondern nur die benötigten Felder berechnet.
Wenn Sie also in vollem Vertrauen laufen können - benutzen Sie einfach C ++ über C ++ / CLI, es funktioniert viel besser.
In den meisten Fällen können Visual Studio und der Compiler die Probleme vor Ihnen verbergen. Mir ist jedoch ein großes Problem bekannt, das auftreten kann, wenn Sie Ihre App so einrichten, dass die Plattform automatisch erkannt wird (x86 vs x64) und auch Abhängigkeiten von 32-Bit-Drittanbieter-DLLs aufweisen. In diesem Fall wird es auf 64-Bit-Plattformen versuchen, die DLLs mit 64-Bit-Konventionen und -Strukturen aufzurufen, und es wird einfach nicht funktionieren.
Sie haben die Portierungsprobleme erwähnt, mit denen Sie sich befassen müssen. Ich kenne Ihre Bewerbung offensichtlich nicht, aber der Versuch, das JIT zu hinterfragen, ist oft eine völlige Zeitverschwendung. Die Leute, die das JIT schreiben, haben ein tiefgehendes Verständnis für die x86 / x64-Chip-Architektur und wissen, was besser ist und was schlechter funktioniert als wahrscheinlich irgendjemand anderer auf dem Planeten.
Ja, es ist möglich, dass Sie einen Eckfall haben, der anders und einzigartig ist, aber wenn Sie gerade dabei sind, eine neue Anwendung zu schreiben, würde ich mir keine Sorgen um das JIT machen Compiler. Es ist wahrscheinlich eine alberne Schleife, die irgendwo vermieden werden kann, die Ihnen die 100fache Leistungsverbesserung bringt, die Sie bekommen, wenn Sie versuchen, das JIT zu hinterfragen. Er erinnert mich an Probleme, die wir beim Schreiben unseres ORM bekommen haben, wir haben uns den Code angeschaut und gedacht, wir könnten ein paar Maschinenanweisungen daraus machen ... natürlich ging der Code dann los und über ein Netzwerk mit einem Datenbankserver verbunden , also trimmten wir Mikrosekunden von einem Prozess ab, der an anderer Stelle von Millisekunden begrenzt wurde.
Universelle Regel der Leistungsoptimierung ... Wenn Sie Ihre Leistung nicht gemessen haben, wissen Sie nicht wo Ihre Engpässe sind, Sie denken nur dass Sie wissen .. und du liegst wahrscheinlich falsch.
Ich glaube, dass der 64 JIT nicht vollständig entwickelt / portiert ist, um die Vorteile dieser 64-Bit-Architektur-CPUs zu nutzen, so dass Probleme auftreten und Sie möglicherweise ein emuliertes Verhalten Ihrer Baugruppen bekommen. Ich würde in Fälle schauen, in denen dies vermieden werden kann und / oder vielleicht sehen, ob es einen guten schnellen 64 C ++ - Compiler gibt, um zeitkritische Berechnungen und Algorithmen zu schreiben. Aber selbst wenn Sie Schwierigkeiten haben, Informationen zu finden, oder keine Zeit haben, den zerlegten Code zu lesen, bin ich mir ziemlich sicher, dass die Beseitigung schwerer Berechnungen außerhalb des verwalteten Codes Ihre Probleme verringern würde. Leistung steigern [etwas sicher, dass Sie das schon tun, aber nur um es zu erwähnen :)]
Ein Profiler sollte Ihre Timing-Ergebnisse nicht signifikant beeinflussen. Wenn die Profiler-Overheads wirklich "signifikant" sind, dann können Sie wahrscheinlich nicht viel mehr Geschwindigkeit aus Ihrem Code herausholen und sollten darüber nachdenken, Ihre Hardware-Engpässe (Festplatte, RAM oder CPU?) und aktualisieren. (Klingt so, als ob Sie an die CPU gebunden sind, das ist also der Anfangspunkt)
Im Allgemeinen befreien .net und JIT Sie von den meisten Portierungsproblemen von 64 Bit. Wie Sie wissen, gibt es Effekte in Bezug auf die Registergröße (Änderungen der Speicherbelegung, Zuordnung zu nativem Code, bei denen alle Teile des Programms native 64-Bit-Builds sein müssen) und einige Leistungsunterschiede (größere Speicherbelegung, mehr Register, breitere Busse) usw.), also kann ich dir nichts mehr erzählen, als du bereits an dieser Front weißt. Die anderen Probleme, die ich gesehen habe, sind OS statt C # Einsen - es gibt jetzt verschiedene Registry-Hives für 64-Bit- und WOW64-Anwendungen zum Beispiel, so dass einige Registry-Zugriffe sorgfältig geschrieben werden müssen.
Es ist im Allgemeinen eine schlechte Idee, sich darüber Gedanken zu machen, was das JIT mit Ihrem Code macht und versuchen, es besser anzupassen, da sich das JIT wahrscheinlich mit .net 4 oder 5 oder 6 ändert und Ihre "Optimierungen" sich ändern können Ineffizienzen oder schlimmer noch, Bugs. Denken Sie auch daran, dass der JIT den Code speziell für die CPU kompiliert, auf der er läuft, sodass eine Verbesserung Ihres Entwicklungs-PCs möglicherweise keine Verbesserung auf einem anderen PC darstellt. Was Sie mit der heutigen JIT auf der heutigen CPU bekommen, kann Sie in ein Jahren beißen, wenn Sie etwas upgraden.
Insbesondere zitieren Sie "Eigenschaften sind nicht auf x64 inlined". Wenn Sie Ihre gesamte Codebase durchlaufen haben und alle Ihre Eigenschaften in Felder umgewandelt haben, kann es sein, dass es einen neuen JIT für 64 Bit gibt, der Inline-Eigenschaften ausführt. In der Tat kann es besser als Ihr "Workaround" Code funktionieren. Lassen Sie Microsoft das für Sie optimieren.
Sie weisen zu Recht darauf hin, dass sich Ihr Speicherprofil ändern kann. So benötigen Sie möglicherweise mehr RAM, schnellere Festplatten für virtuellen Speicher und größere CPU-Caches. Alle Hardwareprobleme. Sie können den Effekt möglicherweise reduzieren, indem Sie (z. B.) Int32 anstelle von int verwenden, aber das macht möglicherweise keinen großen Unterschied und könnte die Leistung beeinträchtigen (da Ihre CPU native 64-Bit-Werte effizienter verarbeiten kann als halbe 32-Bit-Werte) ).
Sie sagen, dass "Startzeiten länger sein können", aber das scheint in einer Anwendung, die Sie sagen, für Stunden bei 100% CPU läuft, eher irrelevant.
Worüber machen Sie sich wirklich Sorgen? Vielleicht mal deinen Code auf einem 32-bit PC und dann mal die gleiche Aufgabe auf einem 64-bit PC. Gibt es eine halbe Stunde Unterschied während einer 4-stündigen Fahrt? Oder ist der Unterschied nur 3 Sekunden? Oder ist der 64-Bit-PC tatsächlich schneller? Vielleicht suchen Sie nach Lösungen für Probleme, die nicht existieren.
Also zurück zu den üblichen, allgemeineren Ratschlägen. Profil und Zeit zur Identifizierung von Engpässen. Sehen Sie sich die Algorithmen und mathematischen Prozesse an, die Sie anwenden, und versuchen Sie, diese durch effizientere zu ersetzen. Stellen Sie sicher, dass Ihr Multithreading-Ansatz Ihrer Leistung eher hilft als schadet (d. H. Wartezeiten und Sperren werden vermieden). Versuchen Sie, Speicherzuweisung / Freigabe zu reduzieren - z. Objekte erneut verwenden, anstatt sie durch neue zu ersetzen. Versuchen Sie, die Verwendung von häufigen Funktionsaufrufen und virtuellen Funktionen zu reduzieren. Wechseln Sie zu C ++ und entfernen Sie die inhärenten Gemeinkosten der Garbage Collection, der Überprüfung von Grenzen usw., die von .net auferlegt werden. Hmmm. Nichts davon hat etwas mit 64 Bit zu tun, oder?