Ich muss zugeben, dass ich mich normalerweise nicht darum gekümmert habe, zwischen den Debug- und Release- Konfigurationen in meinem Programm zu wechseln , und ich habe mich normalerweise für die Debug- Konfiguration entschieden, selbst wenn die Programme tatsächlich beim Kunden bereitgestellt werden.
Soweit ich weiß, besteht der einzige Unterschied zwischen diesen Konfigurationen, wenn Sie sie nicht manuell ändern, darin, dass bei Debug die DEBUG
Konstante definiert und bei Release der Optimierungscode aktiviert ist.
Meine Fragen sind also zweifach:
Gibt es große Leistungsunterschiede zwischen diesen beiden Konfigurationen? Gibt es einen bestimmten Codetyp, der hier große Leistungsunterschiede verursacht, oder ist er tatsächlich nicht so wichtig?
Gibt es irgendeine Art von Code, der unter der Debug- Konfiguration einwandfrei ausgeführt wird und unter der Release- Konfiguration möglicherweise fehlschlägt , oder können Sie sicher sein, dass Code, der unter der Debug- Konfiguration getestet wurde und einwandfrei funktioniert, auch unter der Release-Konfiguration einwandfrei funktioniert.
quelle
Antworten:
Der C # -Compiler selbst ändert die emittierte IL im Release-Build nicht wesentlich. Bemerkenswert ist, dass keine NOP-Opcodes mehr ausgegeben werden, mit denen Sie einen Haltepunkt für eine geschweifte Klammer festlegen können. Der große ist der Optimierer, der in den JIT-Compiler integriert ist. Ich weiß, dass es die folgenden Optimierungen vornimmt:
Methode Inlining. Ein Methodenaufruf wird durch das Einfügen des Codes der Methode ersetzt. Dies ist eine große Sache, es macht Immobilien-Accessoren im Wesentlichen kostenlos.
CPU-Registerzuordnung. Lokale Variablen und Methodenargumente können in einem CPU-Register gespeichert bleiben, ohne jemals (oder weniger häufig) im Stapelrahmen gespeichert zu werden. Dies ist eine große Sache, die das Debuggen von optimiertem Code so schwierig macht. Und dem flüchtigen Schlüsselwort eine Bedeutung geben.
Eliminierung der Array-Indexprüfung. Eine wichtige Optimierung bei der Arbeit mit Arrays (alle .NET-Auflistungsklassen verwenden intern ein Array). Wenn der JIT-Compiler überprüfen kann, dass eine Schleife ein Array niemals außerhalb der Grenzen indiziert, wird die Indexprüfung aufgehoben. Großes.
Schleife abrollen. Schleifen mit kleinen Körpern werden verbessert, indem der Code bis zu viermal im Körper wiederholt wird und weniger Schleifen ausgeführt werden. Reduziert die Verzweigungskosten und verbessert die superskalaren Ausführungsoptionen des Prozessors.
Eliminierung des toten Codes. Eine Aussage wie if (false) {/ ... /} wird vollständig eliminiert. Dies kann durch ständiges Falten und Inlining auftreten. In anderen Fällen kann der JIT-Compiler feststellen, dass der Code keine möglichen Nebenwirkungen hat. Diese Optimierung macht das Profilieren von Code so schwierig.
Code heben. Code innerhalb einer Schleife, der nicht von der Schleife betroffen ist, kann aus der Schleife verschoben werden. Der Optimierer eines C-Compilers wird viel mehr Zeit damit verbringen, Möglichkeiten zum Heben zu finden. Aufgrund der erforderlichen Datenflussanalyse ist dies jedoch eine teure Optimierung, und der Jitter kann sich die Zeit nicht leisten, sodass nur offensichtliche Fälle angehoben werden. Erzwingen, dass .NET-Programmierer besseren Quellcode schreiben und sich selbst hochziehen.
Gemeinsame Eliminierung von Subausdrücken. x = y + 4; z = y + 4; wird z = x; Ziemlich häufig in Anweisungen wie dest [ix + 1] = src [ix + 1]; Zur besseren Lesbarkeit geschrieben, ohne eine Hilfsvariable einzuführen. Die Lesbarkeit muss nicht beeinträchtigt werden.
Ständiges Falten. x = 1 + 2; wird x = 3; Dieses einfache Beispiel wird vom Compiler frühzeitig erkannt, tritt jedoch zur JIT-Zeit auf, wenn andere Optimierungen dies ermöglichen.
Weitergabe kopieren. x = a; y = x; wird y = a; Dies hilft dem Registerzuweiser, bessere Entscheidungen zu treffen. Es ist eine große Sache im x86-Jitter, weil es nur wenige Register gibt, mit denen man arbeiten kann. Die Auswahl der richtigen ist für die Perfektion von entscheidender Bedeutung.
Dies sind sehr wichtige Optimierungen, die einen großen Unterschied machen können, wenn Sie beispielsweise den Debug-Build Ihrer App profilieren und mit dem Release-Build vergleichen. Das ist jedoch nur dann wirklich wichtig, wenn sich der Code auf Ihrem kritischen Pfad befindet. Die 5 bis 10% des Codes, den Sie schreiben, wirken sich tatsächlich auf die Leistung Ihres Programms aus. Der JIT-Optimierer ist nicht intelligent genug, um im Voraus zu wissen, was wichtig ist. Er kann nur das Einstellrad "Drehen auf elf" für den gesamten Code anwenden.
Das effektive Ergebnis dieser Optimierungen für die Ausführungszeit Ihres Programms wird häufig durch Code beeinflusst, der an anderer Stelle ausgeführt wird. Lesen einer Datei, Ausführen einer Datenbankabfrage usw. Die Arbeit des JIT-Optimierers wird vollständig unsichtbar. Es macht aber nichts aus :)
Der JIT-Optimierer ist ein ziemlich zuverlässiger Code, vor allem, weil er millionenfach getestet wurde. Es ist äußerst selten, dass Probleme in der Release-Build-Version Ihres Programms auftreten. Es passiert jedoch. Sowohl der x64- als auch der x86-Jitter hatten Probleme mit Strukturen. Der x86-Jitter hat Probleme mit der Gleitkommakonsistenz und führt zu geringfügig unterschiedlichen Ergebnissen, wenn die Zwischenprodukte einer Gleitkommaberechnung mit einer Genauigkeit von 80 Bit in einem FPU-Register gespeichert werden, anstatt beim Löschen in den Speicher abgeschnitten zu werden.
quelle
LinkedList<T>
nicht, obwohl es nicht sehr oft verwendet wird.volatile
Schlüsselwort gilt nicht für lokale Variablen, die in einem Stapelrahmen gespeichert sind. Aus der Dokumentation unter msdn.microsoft.com/en-us/library/x13ttww7.aspx : "Das Schlüsselwort volatile kann nur auf Felder einer Klasse oder Struktur angewendet werden. Lokale Variablen können nicht als flüchtig deklariert werden."Debug
undRelease
in dieser Hinsicht wirklich ausmacht, ist das Kontrollkästchen "Code optimieren", das normalerweise aktiviert,Release
aber deaktiviert istDebug
. Es soll nur sichergestellt werden, dass die Leser nicht glauben, dass es "magische", unsichtbare Unterschiede zwischen den beiden Build-Konfigurationen gibt, die über das hinausgehen, was auf der Projekteigenschaftsseite in Visual Studio zu finden ist.Ja, es gibt viele Leistungsunterschiede, die wirklich für Ihren gesamten Code gelten. Das Debuggen führt nur sehr wenig Leistungsoptimierung und den Release-Modus sehr wenig durch.
Nur Code, der auf der
DEBUG
Konstante basiert, kann bei einem Release-Build eine andere Leistung erbringen. Außerdem sollten Sie keine Probleme sehen.Ein Beispiel für Framework-Code, der von der
DEBUG
Konstante abhängt, ist dieDebug.Assert()
Methode, für die das Attribut[Conditional("DEBUG)"]
definiert ist. Dies bedeutet, dass dies auch von derDEBUG
Konstante abhängt und dies nicht im Release-Build enthalten ist.quelle
DEBUG
:AppDomain.CurrentDomain.GetAssemblies().Sum(p => p.GetTypes().Sum(p1 => p1.GetProperties().Length))
.asp.net
aktiviert sind und Debug anstelle von Release verwenden, werden möglicherweise einige Skripte auf Ihrer Seite hinzugefügt, z.MicrosoftAjax.debug.js
B .: Das hat ungefähr 7.000 Zeilen.Dies hängt stark von der Art Ihrer Anwendung ab. Wenn Ihre Anwendung UI-lastig ist, werden Sie wahrscheinlich keinen Unterschied bemerken, da der Benutzer die langsamste Komponente ist, die mit einem modernen Computer verbunden ist. Wenn Sie einige Benutzeroberflächenanimationen verwenden, möchten Sie möglicherweise testen, ob Sie beim Ausführen in DEBUG Build eine merkliche Verzögerung feststellen können.
Wenn Sie jedoch viele rechenintensive Berechnungen haben, werden Sie Unterschiede feststellen (können bis zu 40% betragen, wie von @Pieter erwähnt, obwohl dies von der Art der Berechnungen abhängt).
Es ist im Grunde ein Design-Kompromiss. Wenn Sie unter DEBUG-Build freigeben und bei den Benutzern Probleme auftreten, können Sie einen aussagekräftigeren Traceback erhalten und eine viel flexiblere Diagnose durchführen. Durch die Veröffentlichung in DEBUG Build vermeiden Sie auch, dass der Optimierer obskure Heisenbugs erzeugt .
quelle
Ich habe die Erfahrung gemacht, dass mittelgroße oder größere Anwendungen in einem Release-Build deutlich schneller reagieren. Probieren Sie es mit Ihrer Anwendung aus und sehen Sie, wie es sich anfühlt.
Eine Sache, die Sie bei Release-Builds beißen kann, ist, dass Debug-Build-Code manchmal Race-Bedingungen und andere Threading-bezogene Fehler unterdrücken kann. Optimierter Code kann zu einer Neuordnung der Anweisungen führen, und eine schnellere Ausführung kann bestimmte Rennbedingungen verschärfen.
quelle
Sie sollten niemals ein .NET-Debug-Build für die Produktion freigeben. Es kann hässlichen Code enthalten, der das Bearbeiten und Fortfahren unterstützt, oder wer weiß was noch. Soweit ich weiß, geschieht dies nur in VB, nicht in C # (Hinweis: Der ursprüngliche Beitrag ist mit C # gekennzeichnet) , aber es sollte dennoch Anlass geben, darüber nachzudenken, was Microsoft mit einem Debug-Build tun darf. Tatsächlich verliert VB-Code vor .NET 4.0 Speicher, der proportional zur Anzahl der Instanzen von Objekten mit Ereignissen ist, die Sie zur Unterstützung von Edit-and-Continue erstellen. (Obwohl dies gemäß https://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging , dem generierten Code , behoben wurde sieht böse aus, erstellt
WeakReference
Objekte und fügt sie währenddessen einer statischen Liste hinzueine Sperre halten ) Ich möchte auf keinen Fall diese Art von Debugging-Unterstützung in einer Produktionsumgebung!quelle
Nach meiner Erfahrung sind die obskuren "Release Bugs" das Schlimmste, was aus dem Release-Modus herausgekommen ist. Da die IL (Zwischensprache) im Release-Modus optimiert ist, besteht die Möglichkeit von Fehlern, die sich im Debug-Modus nicht manifestiert hätten. Es gibt andere SO-Fragen zu diesem Problem: Häufige Gründe für Fehler in der Release-Version, die im Debug-Modus nicht vorhanden sind
Dies ist mir ein- oder zweimal passiert, wenn eine einfache Konsolen-App im Debug-Modus einwandfrei funktioniert, bei genau derselben Eingabe jedoch im Release-Modus ein Fehler auftritt. Diese Fehler sind EXTREM schwer zu debuggen (ironischerweise per Definition des Release-Modus).
quelle
Ich würde sagen, dass 1) weitgehend von Ihrer Implementierung abhängt. Normalerweise ist der Unterschied nicht so groß. Ich habe viele Messungen durchgeführt und oft konnte ich keinen Unterschied feststellen. Wenn Sie nicht verwalteten Code, viele große Arrays und ähnliches verwenden, ist der Leistungsunterschied etwas größer, aber keine andere Welt (wie in C ++). 2) Normalerweise werden im Release-Code weniger Fehler angezeigt (höhere Toleranz), daher sollte ein Schalter einwandfrei funktionieren.
quelle
quelle