Bei einer Codeüberprüfung mit einem Microsoft-Mitarbeiter stießen wir auf einen großen Codeabschnitt innerhalb eines try{}
Blocks. Sie und ein IT-Vertreter schlugen vor, dass dies Auswirkungen auf die Leistung des Codes haben könnte. Tatsächlich schlugen sie vor, dass der größte Teil des Codes außerhalb von Try / Catch-Blöcken liegen sollte und dass nur wichtige Abschnitte überprüft werden sollten. Der Microsoft-Mitarbeiter fügte hinzu und sagte, ein bevorstehendes Whitepaper warne vor falschen Try / Catch-Blöcken.
Ich habe mich umgesehen und festgestellt, dass dies Auswirkungen auf Optimierungen haben kann , aber es scheint nur zuzutreffen, wenn eine Variable von mehreren Bereichen gemeinsam genutzt wird.
Ich frage nicht nach der Wartbarkeit des Codes oder nach der Behandlung der richtigen Ausnahmen (der betreffende Code muss zweifellos neu faktorisiert werden). Ich beziehe mich auch nicht auf die Verwendung von Ausnahmen für die Flusskontrolle, dies ist in den meisten Fällen eindeutig falsch. Das sind wichtige Themen (einige sind wichtiger), aber nicht der Fokus hier.
Wie wirken sich Try / Catch-Blöcke auf die Leistung aus, wenn keine Ausnahmen ausgelöst werden?
quelle
Antworten:
Prüfen Sie.
Ausgabe:
In Millisekunden:
Neuer Code:
Neue Ergebnisse:
quelle
Nachdem ich alle Statistiken mit try / catch und ohne try / catch gesehen hatte, zwang mich die Neugier, nach hinten zu schauen, um zu sehen, was für beide Fälle generiert wird. Hier ist der Code:
C #:
MSIL:
C #:
MSIL:
Ich bin kein IL-Experte, aber wir können sehen, dass ein lokales Ausnahmeobjekt in der vierten Zeile erstellt wird.
.locals init ([0] class [mscorlib]System.Exception ex)
Danach sind die Dinge ziemlich die gleichen wie bei Methoden ohne try / catch bis zur Zeile siebzehnIL_0021: leave.s IL_002f
. Wenn eine Ausnahme auftritt, springt die Steuerung zur Zeile,IL_0025: ldloc.0
andernfalls springen wir zur BezeichnungIL_002d: leave.s IL_002f
und die Funktionsrückgabe.Ich kann mit Sicherheit davon ausgehen , dass , wenn keine Ausnahmen auftreten , dann ist es der Overhead von lokalen Variablen zu halten Ausnahme Erstellen von Objekten
nurund ein Sprungbefehl.quelle
Nein. Wenn die trivialen Optimierungen, die ein try / finally-Block ausschließt, tatsächlich messbare Auswirkungen auf Ihr Programm haben, sollten Sie .NET wahrscheinlich gar nicht erst verwenden.
quelle
Ziemlich umfassende Erklärung des .NET-Ausnahmemodells.
Rico Marianis Performance-Leckerbissen: Ausnahmekosten: Wann werfen und wann nicht?
Dmitriy Zaslavskiy:
quelle
Die Struktur unterscheidet sich in dem Beispiel von Ben M . Es wird über Kopf innerhalb der inneren
for
Schleife erweitert, was dazu führt, dass es keinen guten Vergleich zwischen den beiden Fällen gibt.Das Folgende ist genauer zum Vergleich, wenn sich der gesamte zu überprüfende Code (einschließlich der Variablendeklaration) im Try / Catch-Block befindet:
Als ich den ursprünglichen Testcode von Ben M ausführte, bemerkte ich einen Unterschied sowohl in der Debug- als auch in der Releas-Konfiguration.
Bei dieser Version bemerkte ich einen Unterschied in der Debug-Version (tatsächlich mehr als in der anderen Version), aber es war kein Unterschied in der Release-Version.
Conclution :
Basierend auf diesen Test, denke ichwir versuchendass sagen kann / Fang hat einen geringen Einfluss aufLeistung haben.
BEARBEITEN:
Ich habe versucht, den Schleifenwert von 10000000 auf 1000000000 zu erhöhen, und bin in Release erneut ausgeführt worden, um einige Unterschiede in der Version zu erhalten. Das Ergebnis war Folgendes:
Sie sehen, dass das Ergebnis nicht relevant ist. In einigen Fällen ist die Version mit Try / Catch tatsächlich schneller!
quelle
try/catch
. Sie planen 12 Versuche / Fänge für den Eintritt in einen kritischen Abschnitt gegen 10 Millionen Schleifen. Das Rauschen der Schleife beseitigt jeden Einfluss, den der Versuch / Fang hat. Wenn Sie stattdessen den Versuch / Fang in die enge Schleife legen und mit / ohne vergleichen, würden Sie die Kosten für den Versuch / Fang erhalten. (Zweifellos ist eine solche Codierung im Allgemeinen keine gute Praxis, aber wenn Sie den Overhead eines Konstrukts zeitlich festlegen möchten, tun Sie dies auf diese Weise). Heutzutage ist BenchmarkDotNet das ideale Tool für zuverlässige Ausführungszeiten.Ich habe die tatsächlichen Auswirkungen von a
try..catch
in einer engen Schleife getestet und es ist zu klein, um in jeder normalen Situation ein Leistungsproblem zu sein.Wenn die Schleife nur sehr wenig Arbeit leistet (in meinem Test habe ich eine durchgeführt
x++
), können Sie die Auswirkungen der Ausnahmebehandlung messen. Die Ausführung der Schleife mit Ausnahmebehandlung dauerte etwa zehnmal länger.Wenn die Schleife tatsächlich arbeitet (in meinem Test habe ich die Int32.Parse-Methode aufgerufen), hat die Ausnahmebehandlung zu wenig Auswirkungen, um messbar zu sein. Ich habe einen viel größeren Unterschied gemacht, indem ich die Reihenfolge der Schleifen vertauscht habe ...
quelle
try catch-Blöcke haben einen vernachlässigbaren Einfluss auf die Leistung, aber die Ausnahme Das Werfen kann ziemlich groß sein. Dies ist wahrscheinlich der Punkt, an dem Ihr Mitarbeiter verwirrt war.
quelle
Der Versuch / Fang hat Auswirkungen auf die Leistung.
Aber es ist keine große Auswirkung. Die Try / Catch-Komplexität ist im Allgemeinen O (1), genau wie eine einfache Zuweisung, außer wenn sie in einer Schleife platziert sind. Sie müssen sie also mit Bedacht einsetzen.
Hier ist eine Referenz zur Try / Catch-Leistung (erklärt die Komplexität zwar nicht, ist aber impliziert). Werfen Sie einen Blick auf den Abschnitt Weniger Ausnahmen werfen
quelle
Theoretisch hat ein Try / Catch-Block keine Auswirkung auf das Codeverhalten, es sei denn, es tritt tatsächlich eine Ausnahme auf. Es gibt jedoch einige seltene Umstände, in denen das Vorhandensein eines Try / Catch-Blocks einen großen Effekt haben kann, und einige ungewöhnliche, aber kaum undurchsichtige, in denen der Effekt spürbar sein kann. Der Grund dafür ist der gegebene Code wie:
Der Compiler kann möglicherweise Anweisung1 basierend auf der Tatsache optimieren, dass die Ausführung von Anweisung2 vor Anweisung3 garantiert ist. Wenn der Compiler erkennt, dass thing1 keine Nebenwirkungen hat und thing2 x nicht verwendet, kann er thing1 sicher ganz weglassen. Wenn [wie in diesem Fall] thing1 teuer wäre, könnte dies eine wesentliche Optimierung sein, obwohl die Fälle, in denen thing1 teuer ist, auch diejenigen sind, die der Compiler am wenigsten optimieren würde. Angenommen, der Code wurde geändert:
Jetzt gibt es eine Folge von Ereignissen, bei denen Anweisung3 ausgeführt werden könnte, ohne dass Anweisung2 ausgeführt wurde. Selbst wenn nichts im Code für
thing2
eine AusnahmeInterlocked.CompareExchange
auslösen könnte, könnte ein anderer Thread eine verwenden, um zu bemerken, dassq
gelöscht und auf gesetzt wurdeThread.ResetAbort
, und dann eineThread.Abort()
Voranweisung ausführen, in die Anweisung2 ihren Wert geschrieben hatx
. Danncatch
würde dasThread.ResetAbort()
[über einen Delegatenq
] ausgeführt, so dass die Ausführung mit Anweisung3 fortgesetzt werden kann. Eine solche Abfolge von Ereignissen wäre natürlich außerordentlich unwahrscheinlich, aber ein Compiler muss Code generieren, der gemäß der Spezifikation funktioniert, selbst wenn solche unwahrscheinlichen Ereignisse auftreten.Im Allgemeinen ist es viel wahrscheinlicher, dass der Compiler Möglichkeiten bemerkt, einfache Codebits wegzulassen als komplexe, und daher ist es selten, dass ein Versuch / Fang die Leistung stark beeinträchtigt, wenn niemals Ausnahmen ausgelöst werden. Es gibt jedoch einige Situationen, in denen das Vorhandensein eines Try / Catch-Blocks Optimierungen verhindern kann, die - ohne Try / Catch - eine schnellere Ausführung des Codes ermöglicht hätten.
quelle
Obwohl " Prävention besser ist als Handhabung ", könnten wir im Hinblick auf Leistung und Effizienz den Try-Catch der Pre-Varication vorziehen. Betrachten Sie den folgenden Code:
Hier ist das Ergebnis:
quelle
In der Diskussion zur Try / Catch-Implementierung finden Sie eine Diskussion darüber, wie Try / Catch-Blöcke funktionieren und wie einige Implementierungen einen hohen Overhead und andere keinen Overhead haben, wenn keine Ausnahmen auftreten. Insbesondere denke ich, dass die Windows 32-Bit-Implementierung einen hohen Overhead hat und die 64-Bit-Implementierung nicht.
quelle