2009-03-11 9 views
8

Welche ungewöhnlichen, unerwarteten Konsequenzen sind in Bezug auf Leistung, Speicher usw. aufgetreten, wenn Sie vom Ausführen Ihrer .NET-Anwendungen unter dem 64-Bit-JIT vs. dem 32-Bit-JIT wechseln? Ich interessiere mich für das Gute, interessiere mich aber mehr für die überraschend schlechten Probleme, denen die Leute begegnet sind.Meine 32-Bit-Kopfschmerzen sind jetzt eine 64-Bit-Migräne?!? (oder 64bit .NET CLR Runtime-Probleme)

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 von der "gotchas" from a programming/porting standpoint betroffen. (dh: natives/COM-Interop korrekt behandeln, Referenztypen, die in Strukturen eingebettet sind, die die Größe der Struktur ändern, usw.)

Aber this question and it's answer hat mich zum Nachdenken gebracht - Welche anderen Probleme übersehe ich?

Es gab viele Fragen und Blogeinträge, die sich um dieses Problem drehen, oder einen Aspekt davon treffen, aber ich habe nichts gesehen, das eine anständige Liste von Problemen zusammengestellt hat.

Insbesondere - Meine Anwendung ist sehr CPU-gebunden und hat riesige Speicherverbrauchsmuster (daher die Notwendigkeit für 64bit an erster Stelle), sowie von Natur aus grafisch sein. 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 ein paar Fragen, die ich zur Zeit bewusst bin:

Ich würde gerne wissen, was andere, spezifisch, Menschen gibt habe ich im JIT auf 64bit Windows entdeckt, und auch wenn es Workarounds für die Performance gibt.

Danke euch allen!

---- EDIT -----

Nur um zu klären -

ich weiß, dass früh zu optimieren versuchen oft schlecht. 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 verbringe viel Zeit Profiling der Anwendung, 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.

+0

Dieser Thread wäre viel nützlicher (einfach mit Google oder Stacko-Suche, usw. zu finden), wenn der Titel auf die .NET 32- und 64-Bit-Laufzeiten bezogen. –

Antwort

3

Ich erinnere mich an ein Problem von einem IRC-Kanal, den ich häufig höre. Es optimiert die temporäre Kopie in diesem Fall weg:

EventHandler temp = SomeEvent; 
if(temp != null) 
{ 
    temp(this, EventArgs.Empty); 
} 

Putting das Rennen wieder in Hoch und mögliche Nullreferenz Ausnahmen verursachen.

+0

Interessant .... Ist eine Optimierung nur auf 64-Bit-JIT möglich, oder geschieht dies auch auf dem 32-Bit-JIT? –

+0

Das passiert nicht in 32bit.Es war nicht meine Konversation, also habe ich keine Möglichkeit, dies zu bestätigen, aber die Konversation dauerte eine gute Stunde oder so, es sei denn, es gibt einen anderen 64-Bit-Jitter, könnte es der sein, an dem du arbeitest – Quibblesome

+0

IIRC Der 32bit Jitter entspricht in diesem Fall eigentlich nicht der Spezifikation und sollte auf diese Weise ohnehin optimiert werden. Aber das ist ein Trick, der verwendet wird, um Race Conditions zu verhindern, wenn Ereignisse ausgelöst und über unterschiedliche Threads enthookt werden. – Quibblesome

-1

Ich bin nicht so vertraut mit 64-Bit-Ausgaben, aber ich habe einen Kommentar haben:

Wir über kleine Effizienzen vergessen sollte, sagen wir etwa 97% der Zeit: vorzeitige Optimierung ist die Wurzel allen Übels. - Donald Knuth

+4

Aber es gibt immer diese verdammten 3% ... –

+0

Wie gesagt, meine Anwendung ist enorm CPU-gebunden. Ich habe Prozesse mit 5-Stunden-Laufzeiten. Die Kehrseite deines Kommentars ist, dass es in 3% der Fälle nicht die Wurzel allen Übels ist. Nehmen Sie Rico Marianis Kommentar dazu - wenn es nur 3% der Zeit zählt, bedeutet dies, dass eine Zeile in 33 Code-Angelegenheiten für die Optimierung wichtig ist. –

+0

Aus Neugier, werden diese Probleme immer noch angezeigt, wenn Sie 64-Bit-Plattformen in VS lieber als die Standardeinstellung Any CPU? – Powerlord

1

Die meiste Zeit Visual Studio und der Compiler machen einen ziemlich guten Job, die Probleme von Ihnen zu verstecken. Allerdings ist mir ein großes Problem bekannt, das auftreten kann, wenn Sie Ihre App so einstellen, dass die Plattform (x86 vs x64) und auch Abhängigkeiten von 32-Bit-Drittanbieter-DLLs erkennt. In diesem Fall wird es auf 64bit-Plattformen versuchen, die dlls unter Verwendung von 64-Bit-Konventionen und -Strukturen aufzurufen, und es wird einfach nicht funktionieren.

+0

Ja - ich bin nicht so besorgt über diese Art von Problemen. Mir geht es eher um Performance/Speicher/Runtime-Probleme, die die versteckten Probleme sind. –

+0

+1 - Ich habe dieses Problem mit einer meiner dritten Parteibibliotheken gefunden. Ich muss sowohl 32- als auch 64-Bit-Versionen in mein Installationsprogramm aufnehmen und die entsprechende Version installieren. –

1

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 der 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 ein Eckgehäuse haben, das anders und einzigartig ist, aber wenn Sie "gerade eine neue Anwendung schreiben", dann würde ich mir keine Sorgen über den JIT-Compiler machen. 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.

Universal-Regel der Leistung Zwicken ... Wenn Sie Ihre Leistung nicht gemessen haben Sie nicht wissen, wo Ihre Engpässe sind, die Sie gerade denken wissen Sie ... und Sie sind wahrscheinlich falsch.

+0

Walden: Ich stimme zu. Meine App ist jedoch sehr CPU-gebunden. Es ist hochgradig mathematisch und hat viele mehrstündige Laufzeitprozesse. Ich verbringe viel Zeit damit, feine Details zu profilieren und zu optimieren, was sehr hilfreich sein kann. Profiler sind jedoch schwierig, da sie JIT-Probleme deaktivieren. –

1

über Quibblesome Antwort:

Ich habe versucht, den folgenden Code in meinem Windows 7 x64 in Release-Modus ohne Debugger auszuführen, und Nullreferenceexception wurde nie geworfen.

using System; 
using System.Threading; 

namespace EventsMultithreadingTest 
{ 
    public class Program 
    { 
     private static Action<object> _delegate = new Action<object>(Program_Event); 
     public static event Action<object> Event; 

     public static void Main(string[] args) 
     { 
      Thread thread = new Thread(delegate() 
       { 
        while (true) 
        { 
         Action<object> ev = Event; 

         if (ev != null) 
         { 
          ev.Invoke(null); 
         } 
        } 
       }); 
      thread.Start(); 

      while (true) 
      { 
       Event += _delegate; 
       Event -= _delegate; 
      } 
     } 

     static void Program_Event(object obj) 
     { 
      object.Equals(null, null); 
     } 
    } 
} 
+2

Dieses Problem bestand nur in .NET 1.x auf x64; Dies ist seit der Einführung des .NET 2.0 Memory Model im Jahr 2005 kein Problem mehr. siehe http://code.logos.com/blog/2008/11/events_and_threads_part_4.html und http://msdn.microsoft.com/magazine/cc163715.aspx –

0

Ich glaube, die 64 JIT nicht vollständig/portierte entwickelt Vorteil der solche 64-Bit-Architektur CPUs zu nehmen, damit sie Probleme hat, können Sie "emuliert das Verhalten Ihrer Baugruppen werden immer die Probleme und unerwartete verursachen Verhalten. 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, um den Code zu lesen, bin ich mir ziemlich sicher, dass das Herausnehmen schwerer Berechnungen außerhalb des verwalteten Codes Ihre Probleme verringern würde [etwas sicher, dass Sie dies bereits tun Aber nur um zu erwähnen :)]

0

Ein Profiler sollte nicht signifikant beeinflussen Ihre Timing-Ergebnisse. Wenn die Profiler-Gemeinkosten wirklich "" "signifikant" sind, dann können Sie wahrscheinlich nicht viel mehr Geschwindigkeit aus Ihrem Code herausquetschen, und sollten darüber nachdenken, Ihre Hardware-Engpässe (Festplatte, RAM oder CPU?) Und Upgrades zu betrachten. (Hört sich an, als ob Sie CPU-gebunden sind, also starten Sie)

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 und nicht C# - 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 generell eine schlechte Idee, sich Gedanken darüber zu machen, was der JIT mit Ihrem Code machen wird und versuchen, ihn besser anzupassen, da sich der JIT mit .net 4 oder 5 oder 6 ändern wird und Ihre "Optimierungen" möglicherweise zu Ineffizienzen oder schlimmeren Fehlern werden. 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 dem heutigen JIT auf der heutigen CPU hinbekommen, kann Sie in den Jahren, in denen Sie etwas aufrüsten, in Mitleidenschaft ziehen.

Spezifisch 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 "Startzeiten können länger sein", aber das scheint eher irrelevant in einer Anwendung, die Sie sagen, läuft für Stunden bei 100% CPU.

Worüber sind Sie wirklich besorgt? 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 es nicht gibt.

Also zurück zu den üblichen, generischen, Rat. 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?

4

Ein besonders problematisches Leistungsproblem in.NET bezieht sich auf den Armen JIT:

https://connect.microsoft.com/VisualStudio/feedback/details/93858/struct-methods-should-be-inlined?wa=wsignin1.0

Grundsätzlich inlining und structs arbeiten nicht gut zusammen auf x64 (obwohl diese Seite schlägt inlining funktioniert jetzt aber nachfolgende redunant Kopien werden nicht beseitigt, dass Klänge Verdacht auf die winzige Perf. Differenz).

In jedem Fall, nach Wrestling mit .NET lang genug für diese, ist meine Lösung C++ für alles verwenden, numerisch intensiv. Selbst in "guten" Fällen für .NET, wo Sie nicht mit Strukturen arbeiten und Arrays verwenden, bei denen die Grenzenüberprüfung optimiert ist, schlägt C++ .NET hands down.

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 Dinge manuell einfügen müssen und/oder keine Generika verwenden können) und viel langsamer.

Ich habe auf Eigen in C++ umgeschaltet: es ist absolut großartig, was zu lesbarem Code und hoher Leistung führt; Ein dünner C++/CLI-Wrapper bietet dann die Verbindung zwischen der Compute-Engine und der .NET-Welt.

Eigen funktioniert durch Vorlage Meta-Programmierung; 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.

So zum Beispiel, wenn P eine Matrix ist, funktioniert diese Art von Sachen einfach:

1.0/(P.transpose() * P).diagonal().sum(); 

..., die keine zeitweise umgesetzt Variante von P zuordnet und berechnen nicht die ganzes Matrixprodukt, aber nur die Felder, die es benötigt.

Also, wenn Sie in voller Vertrauen ausführen können - nur C++ über C++/CLI verwenden, funktioniert es viel besser.

Verwandte Themen