Nachdem ich hier eine Frage darüber gelesen hatte, welche Dinge unser Computer in einer Sekunde erledigen könnte, machte ich einen kleinen Test, den ich für eine Weile im Hinterkopf hatte, und ich bin sehr überrascht von den Ergebnissen. Schau:
Einfaches Programm, um eine Null-Ausnahme zu erfassen, dauert fast eine Sekunde, um 1900 Iterationen durchzuführen:
%Vor%Alternativ kann vor der Zuweisung überprüft werden, ob test == null ist, das gleiche Programm kann 200000000 Iterationen in einer Sekunde ausführen.
%Vor%Jeder hat eine detaillierte Erklärung, warum dieser RIESIGE Unterschied?
BEARBEITEN: Wenn ich den Test im Freigabemodus außerhalb von Visual Studio durchführe, erhalte ich 35000-40000 Iterationen gegenüber 400000000 Iterationen (immer ungefähr)
Hinweis Ich führe das mit einem beschissenen PIV 3.06Ghz
Es gibt keine Möglichkeit, die für 1900 Iterationen eine Sekunde dauern sollte, es sei denn, Sie laufen im Debugger. Das Ausführen von Leistungstests unter dem Debugger ist eine schlechte Idee.
BEARBEITEN: Beachten Sie, dass dies nicht der Fall ist, wenn Sie zum Release build wechseln - es ist ein Fall, ohne den Debugger zu laufen; d. h. Drücken von Strg-F5 anstelle von F5 .
Nachdem das gesagt wurde, ist es auch eine schlechte Idee, Ausnahmen zu provozieren, wenn man sie sehr leicht vermeiden kann.
Ich gehe von der Durchführung von Ausnahmen aus: Wenn Sie sie ordnungsgemäß verwenden, sollten sie keine wesentlichen Leistungsprobleme verursachen, außer Sie befinden sich in einer katastrophalen Situation (z. B. wenn Sie es versuchen) machen Hunderttausende von Web-Service-Anrufen und das Netzwerk ist ausgefallen).
Ausnahmen sind teuer unter Debuggern - jedenfalls in Visual Studio sowieso - wegen der Ausarbeitung, ob man in den Debugger usw. einbricht oder nicht, und wahrscheinlich jede Menge Stapelanalyse, die sonst unnötig ist. Sie sind immer noch etwas teuer, aber Sie sollten nicht genug von ihnen werfen, um es zu bemerken. Es gibt immernoch einen Stack-Abbau, relevante Catch-Handler zu finden, usw. - aber das sollte nur passieren, wenn etwas nicht stimmt.
BEARBEITEN: Sicher, das Auslösen einer Ausnahme wird Ihnen immer weniger Iterationen pro Sekunde geben (obwohl 35000 immer noch eine sehr niedrige Zahl ist - ich würde über 100 K erwarten), weil Sie fast nichts im Nicht-Ausnahme-Fall. Schauen wir uns die beiden an:
Nicht-Ausnahmeversion des Schleifenkörpers
(Wie in den Kommentaren erwähnt, ist es durchaus möglich, dass das JIT das sowieso weiter optimiert ...)
Ausnahmeversion:
Ist es ein Wunder, dass Sie weniger Leistung sehen?
Vergleichen Sie das nun mit der häufigeren Situation, in der Sie eine ganze Menge Arbeit erledigen, möglicherweise IO, Objekterstellung usw. - und vielleicht eine Ausnahme ausgelöst wird. Dann wird der Unterschied viel weniger signifikant.
Sehen Sie sich Chris Brummes Blog mit besonderem Augenmerk auf an Performance und Trends Abschnitt für eine Erklärung, warum Ausnahmen langsam sind. Sie werden aus einem Grund als "Ausnahmen" bezeichnet: Sie sollten nicht sehr oft vorkommen.
Sie können diese beliebte Frage auch hilfreich finden: Wie langsam sind .NET-Ausnahmen?
Hier ist ein weiterer Faktor. Wenn die PDB-Datei im ausführenden Verzeichnis vorhanden ist, liest die .NET-Laufzeitumgebung beim Auslösen der Ausnahme die PDB-Datei, um die Codezeilennummer in die Ablaufverfolgung für den Ausnahme-Stack aufzunehmen. Das dauert ziemlich viel Zeit. Versuchen Sie Ihre erste Methode (die mit Ausnahme) mit und ohne die .pdb-Datei im ausführenden Verzeichnis.
Ich hatte einen einfachen Timing-Test mit und ohne die .pdb als Antwort auf eine andere Frage durchgeführt, hier .
Eine Optimierung, die der Compiler durchführt, und ich glaube, es könnte "tote Code-Eliminierung" sein; auch abhängig von dem Compiler, den Sie verwenden, tut letzteres Programm tatsächlich, was Assembler folk ein "no-op" nennt.
In meinen Tests ist der "außergewöhnliche" Code nicht so langsam - viel langsamer, aber nicht so viel. Der Unterschied liegt darin, das Objekt "Exception" (oder genauer gesagt "NullReferenceException") zu erstellen. Der langsamste Teil ist das Abrufen der Zeichenfolge für die Ausnahmebedingungsnachricht - es gibt einen internen Aufruf von GetResourceString - und das Abrufen des Stack-Trace.
Dies ist ein schrecklicher Mikro-Benchmark.
Die letztere "optimierte" Schleife hat als eine Kompilierzeit-Invariante, dass der Test immer Null ist, so dass es nicht nötig ist, das Kompilieren in der versuchten Zuweisung zu stören. Sie testen eine leere Schleife, indem Sie jedes Mal eine Ausnahme auslösen.
Ein wirklich guter jit könnte sogar die Schleife komplett entfernen, wobei er feststellt, dass die Schleife keinen Körper hat, also keine Nebenwirkungen, die den Zähler nicht erhöhen und dass der Zähler selbst ungenutzt ist (dies ist unwahrscheinlich, da eine solche Optimierung hätte wenig Dienstprogramm in der realen Welt).
Ausnahmeregelungen sind relativ teuer (in Bezug auf den herkömmlichen Verzweigungskontrollfluss) [1], hauptsächlich aufgrund von drei Dingen:
Das Werfen und Abfangen von Ausnahmen innerhalb einer engen Schleife ist fast sicher ein massiv fehlerhaftes Design, aber wenn Sie diese Wirkung messen wollen, sollten Sie eine Schleife schreiben, die das tatsächlich tut.
Tags und Links c# exception-handling performance