Wie langsam sind .NET-Ausnahmen?

143

Ich möchte keine Diskussion darüber, wann Ausnahmen gemacht werden sollen und wann nicht. Ich möchte ein einfaches Problem lösen. In 99% der Fälle dreht sich das Argument, keine Ausnahmen zu werfen, darum, dass sie langsam sind, während die andere Seite (mit Benchmark-Test) behauptet, dass die Geschwindigkeit nicht das Problem ist. Ich habe zahlreiche Blogs, Artikel und Beiträge gelesen, die sich auf die eine oder andere Seite beziehen. Also was ist es?

Einige Links aus den Antworten: Skeet , Mariani , Brumme .

Goran
quelle
13
Es gibt Lügen, verdammte Lügen und Maßstäbe. :)
gbjbaanb
Leider haben einige Antworten mit hoher Abstimmung hier übersehen, dass die Frage lautet: "Wie langsam sind Ausnahmen?", Und speziell darum gebeten, das Thema zu vermeiden, wie oft sie verwendet werden sollen. Eine einfache Antwort auf die tatsächlich gestellte Frage lautet ..... Unter Windows CLR sind Ausnahmen 750-mal langsamer als Rückgabewerte.
David Jeske

Antworten:

207

Ich bin auf der "nicht langsamen" Seite - oder genauer gesagt "nicht langsam genug, damit es sich lohnt, sie bei normalem Gebrauch zu vermeiden". Ich habe zwei kurze Artikel darüber geschrieben. Es gibt Kritikpunkte am Benchmark-Aspekt, die hauptsächlich darauf zurückzuführen sind, dass "im wirklichen Leben mehr Stapel durchlaufen werden müssen, sodass Sie den Cache sprengen usw." - aber die Verwendung von Fehlercodes, um sich den Stapel hochzuarbeiten, würde dies auch tun den Cache sprengen, deshalb sehe ich das nicht als besonders gutes Argument.

Nur um es klar zu machen - ich unterstütze die Verwendung von Ausnahmen, bei denen sie nicht logisch sind, nicht. Zum Beispiel int.TryParseist es völlig geeignet, Daten von einem Benutzer zu konvertieren. Es ist angemessen, wenn Sie eine maschinengenerierte Datei lesen, bei der ein Fehler bedeutet: "Die Datei hat nicht das Format, das sie haben soll. Ich möchte wirklich nicht versuchen, damit umzugehen, da ich nicht weiß, was sonst noch falsch sein könnte." ""

Bei der Verwendung von Ausnahmen unter "nur vernünftigen Umständen" habe ich noch nie eine Anwendung gesehen, deren Leistung durch Ausnahmen erheblich beeinträchtigt wurde. Grundsätzlich sollten Ausnahmen nicht oft auftreten, es sei denn, Sie haben signifikante Korrektheitsprobleme, und wenn Sie signifikante Korrektheitsprobleme haben, ist die Leistung nicht das größte Problem, mit dem Sie konfrontiert sind.

Jon Skeet
quelle
2
Leider wird den Leuten gesagt, dass Ausnahmen kostenlos sind. Verwenden Sie sie für triviale "korrekte" Funktionen. Sie sollten verwendet werden, wie Sie sagen, wenn etwas schief gelaufen ist - unter "außergewöhnlichen" Umständen
gbjbaanb
4
Ja, die Leute sollten sich auf jeden Fall bewusst sein, dass mit der unangemessenen Verwendung von Ausnahmen Leistungskosten verbunden sind. Ich denke, es ist kein Thema , wenn sie werden in geeigneter Weise :) verwendet
Jon Skeet
7
@PaulLockwood: Ich würde sagen, wenn Sie mehr als 200 Ausnahmen pro Sekunde haben , missbrauchen Sie Ausnahmen. Es ist eindeutig kein "außergewöhnliches" Ereignis, wenn es 200 Mal pro Sekunde auftritt. Beachten Sie den letzten Satz der Antwort: "Grundsätzlich sollten Ausnahmen nicht oft auftreten, es sei denn, Sie haben signifikante Korrektheitsprobleme, und wenn Sie signifikante Korrektheitsprobleme haben, ist die Leistung nicht das größte Problem, mit dem Sie konfrontiert sind."
Jon Skeet
4
@PaulLockwood: Mein Punkt ist, dass wenn Sie mehr als 200 Ausnahmen pro Sekunde haben, dies wahrscheinlich bereits anzeigt, dass Sie Ausnahmen missbrauchen. Es überrascht mich nicht, dass dies häufig der Fall ist, aber es bedeutet, dass der Leistungsaspekt nicht mein erstes Anliegen ist - der Missbrauch von Ausnahmen wäre. Sobald ich alle unangemessenen Verwendungen von Ausnahmen entfernt hatte, würde ich nicht erwarten, dass sie einen wesentlichen Teil der Leistung ausmachen.
Jon Skeet
3
@ DavidJeske: Du hast den Punkt der Antwort verpasst. Das Auslösen einer Ausnahme ist offensichtlich viel langsamer als das Zurückgeben eines normalen Werts. Niemand argumentiert das. Die Frage ist, ob sie zu langsam sind. Wenn Sie sich in einer geeigneten Situation befinden, um eine Ausnahme auszulösen , und dies ein Leistungsproblem verursacht, haben Sie wahrscheinlich größere Probleme - da dies darauf hindeutet, dass mit Ihrem System eine große Menge an Fehlern vorliegt. Normalerweise ist das Problem wirklich , dass Sie Ausnahmen verwenden , wenn sie ungeeignet sind , mit zu beginnen.
Jon Skeet
31

Darauf gibt es die endgültige Antwort desjenigen, der sie implementiert hat - Chris Brumme. Er hat einen ausgezeichneten Blog-Artikel über das Thema geschrieben (Warnung - es ist sehr lang) (Warnung2 - es ist sehr gut geschrieben, wenn Sie ein Technikfreak sind, werden Sie es bis zum Ende lesen und müssen dann Ihre Stunden nach der Arbeit nachholen :) )

Die Zusammenfassung: Sie sind langsam. Sie sind als Win32-SEH-Ausnahmen implementiert, sodass einige sogar die Ring-0-CPU-Grenze überschreiten! In der realen Welt werden Sie natürlich eine Menge anderer Arbeiten ausführen, sodass die eine oder andere Ausnahme überhaupt nicht bemerkt wird. Wenn Sie sie jedoch für den Programmfluss verwenden, sollten Sie damit rechnen, dass Ihre App gehämmert wird. Dies ist ein weiteres Beispiel für die MS-Marketingmaschine, die uns einen schlechten Dienst leistet. Ich erinnere mich an eine Microsoft, die uns erzählte, wie sie absolut keinen Overhead verursacht haben, was völlig zu kurz kommt.

Chris gibt ein passendes Zitat:

Tatsächlich verwendet die CLR intern Ausnahmen, selbst in den nicht verwalteten Teilen des Motors. Es gibt jedoch ein ernstes langfristiges Leistungsproblem mit Ausnahmen, das bei Ihrer Entscheidung berücksichtigt werden muss.

gbjbaanb
quelle
Ich kann dies in realen Tests erwähnen , bei denen ein nullbarer Typ dazu führt, dass eine Ausnahme in einem "Dies ist ein normaler Programmablauf" mehrmals ausgelöst wird, was zu erheblichen Leistungsproblemen geführt hat. Alwyas denken Sie daran, Ausnahmen sind für Ausnahmefälle, glauben Sie niemandem, der etwas anderes sagt, oder Sie werden mit einem Github-Thread wie diesem enden!
Gbjbaanb
8

Ich habe keine Ahnung, wovon die Leute sprechen, wenn sie sagen, dass sie nur langsam sind, wenn sie geworfen werden.

BEARBEITEN: Wenn keine Ausnahmen ausgelöst werden, bedeutet dies, dass Sie eine neue Ausnahme () oder ähnliches ausführen. Andernfalls führt die Ausnahme dazu, dass der Thread angehalten und der Stapel ausgeführt wird. Dies mag in kleineren Situationen in Ordnung sein, aber auf stark frequentierten Websites führt das Verlassen auf Ausnahmen als Workflow- oder Ausführungspfadmechanismus sicherlich zu Leistungsproblemen. Ausnahmen an sich sind nicht schlecht und nützlich, um außergewöhnliche Bedingungen auszudrücken

Der Ausnahme-Workflow in einer .NET-App verwendet Ausnahmen der ersten und zweiten Chance. Für alle Ausnahmen wird das Ausnahmeobjekt weiterhin erstellt, auch wenn Sie sie abfangen und behandeln, und das Framework muss den Stapel noch durchlaufen, um nach einem Handler zu suchen. Wenn Sie fangen und erneut werfen, wird das natürlich länger dauern - Sie werden eine Ausnahme der ersten Chance erhalten, sie fangen, erneut werfen und eine weitere Ausnahme der ersten Chance verursachen, die dann keinen Handler findet, der dann verursacht eine Ausnahme der zweiten Chance.

Ausnahmen sind auch Objekte auf dem Heap. Wenn Sie also Unmengen von Ausnahmen auslösen, verursachen Sie sowohl Leistungs- als auch Speicherprobleme.

Laut meiner vom ACE-Team verfassten Kopie von "Leistungstests von Microsoft .NET-Webanwendungen":

"Die Ausnahmebehandlung ist teuer. Die Ausführung des beteiligten Threads wird angehalten, während die CLR auf der Suche nach dem richtigen Ausnahmebehandler durch den Aufrufstapel rekursiert. Wenn sie gefunden wird, müssen der Ausnahmebehandler und einige Anzahl von Endblöcken alle die Möglichkeit haben, ausgeführt zu werden bevor die reguläre Verarbeitung durchgeführt werden kann. "

Meine eigenen Erfahrungen auf diesem Gebiet haben gezeigt, dass die Reduzierung von Ausnahmen die Leistung erheblich verbessert. Natürlich gibt es noch andere Dinge, die Sie beim Leistungstest berücksichtigen - wenn beispielsweise Ihre Festplatten-E / A-Vorgänge ausgeführt werden oder Ihre Abfragen in Sekundenschnelle erfolgen, sollte dies Ihr Fokus sein. Das Finden und Entfernen von Ausnahmen sollte jedoch ein wesentlicher Bestandteil dieser Strategie sein.

Cory Foy
quelle
1
Nichts, was Sie geschrieben haben, widerspricht der Behauptung, dass Ausnahmen nur langsam sind, wenn sie ausgelöst werden. Sie haben nur über Situationen gesprochen , wo sie sind geworfen. Wenn Sie die Leistung durch Entfernen von Ausnahmen "erheblich verbessert" haben: 1) Waren dies echte Fehlerbedingungen oder nur Benutzerfehler ?
Jon Skeet
2) Sind Sie unter dem Debugger gelaufen oder nicht?
Jon Skeet
Das einzig mögliche, was Sie mit einer Ausnahme tun können, wenn Sie sie nicht auslösen, ist, sie als Objekt zu erstellen, was bedeutungslos ist. Unter dem Debugger zu sein oder nicht, spielt keine Rolle - es wird immer noch langsamer. Ja, es gibt Hooks, die mit einem angehängten Debugger passieren, aber es ist immer noch langsam
Cory Foy
4
Ich weiß - ich war Teil des Premier-Teams bei MSFT. :) Sagen wir einfach, viele - Tausende pro Sekunde in einigen extremen Fällen, die wir gesehen haben. Es gibt nichts Schöneres, als sich mit einem Live-Debugger zu verbinden und Ausnahmen so schnell wie möglich zu sehen. Exs sind langsam - ebenso wie das Herstellen einer Verbindung zu einer Datenbank. Sie tun dies, wenn es sinnvoll ist.
Cory Foy
5
Cory, ich denke, der Punkt von "nur langsam, wenn sie geworfen werden" ist, dass Sie sich keine Sorgen um die Leistung machen müssen, weil nur catch / finally-Blöcke vorhanden sind. Das heißt, diese an sich verursachen keinen Leistungseinbruch, sondern nur das Auftreten einer tatsächlichen Ausnahmeinstanz.
Ian Horwill
6

Das Argument, wie ich es verstehe, ist nicht, dass das Auslösen von Ausnahmen schlecht ist, sie sind per se langsam. Stattdessen geht es darum, das throw / catch-Konstrukt als erstklassige Methode zur Steuerung der normalen Anwendungslogik zu verwenden, anstatt herkömmlicherer bedingter Konstrukte.

In der normalen Anwendungslogik führen Sie häufig Schleifen durch, bei denen dieselbe Aktion tausende / millionenfach wiederholt wird. In diesem Fall können Sie mit einigen sehr einfachen Profilen (siehe Stoppuhr-Klasse) selbst sehen, dass das Auslösen einer Ausnahme anstelle einer einfachen if-Anweisung wesentlich langsamer ausfallen kann.

Tatsächlich habe ich einmal gelesen, dass das .NET-Team von Microsoft die TryXXXXX-Methoden in .NET 2.0 für viele der Basis-FCL-Typen eingeführt hat, insbesondere weil Kunden sich darüber beschwerten, dass die Leistung ihrer Anwendungen so langsam war.

In vielen Fällen stellte sich heraus, dass Kunden versuchten, Werte in einer Schleife vom Typ zu konvertieren, und jeder Versuch fehlschlug. Eine Konvertierungsausnahme wurde ausgelöst und dann von einem Ausnahmebehandler abgefangen, der die Ausnahme verschluckte und die Schleife fortsetzte.

Microsoft empfiehlt jetzt, die TryXXX-Methoden besonders in dieser Situation zu verwenden, um solche möglichen Leistungsprobleme zu vermeiden.

Ich könnte mich irren, aber es scheint, als ob Sie sich nicht sicher sind, ob die "Benchmarks", über die Sie gelesen haben, wahr sind. Einfache Lösung: Probieren Sie es selbst aus.

Asche
quelle
Ich dachte, dass intern diese "try" -Funktionen auch Ausnahmen verwenden?
Greg
1
Diese "Try" -Funktionen lösen intern keine Ausnahmen aus, wenn der Eingabewert nicht analysiert werden kann. Sie lösen jedoch weiterhin Ausnahmen für andere Fehlersituationen aus, z. B. ArgumentException.
Ash
Ich denke, diese Antwort kommt dem Kern des Problems näher als jede andere. Die Aussage "Ausnahmen nur unter vernünftigen Umständen verwenden" beantwortet die Frage nicht wirklich. Die eigentliche Erkenntnis ist, dass die Verwendung von C # -Ausnahmen für den Kontrollfluss viel langsamer ist als die üblichen bedingten Konstrukte. Ihnen könnte vergeben werden, wenn Sie anders denken. In OCaml sind Ausnahmen mehr oder weniger ein GOTO und die akzeptierte Methode zur Implementierung von Unterbrechungen bei Verwendung der zwingenden Funktionen. In meinem speziellen Fall führte das Ersetzen von int.Parse () plus try / catch in einer engen Schleife gegenüber int.TryParse () zu einer signifikanten Leistungssteigerung.
Hugh W
4

Mein XMPP-Server hat einen erheblichen Geschwindigkeitsschub erhalten (sorry, keine tatsächlichen Zahlen, rein beobachtend), nachdem ich konsequent versucht habe, sie zu verhindern (z. B. zu überprüfen, ob ein Socket angeschlossen ist, bevor ich versuche, mehr Daten zu lesen) und mir Möglichkeiten gegeben habe, sie zu vermeiden (die genannten TryX-Methoden). Das war mit nur etwa 50 aktiven (Chat-) virtuellen Benutzern.

Jonathan C Dickinson
quelle
3
Zahlen wären leider nützlich :( Dinge wie Socket-Operationen sollten die Ausnahmekosten bei weitem überwiegen, sicherlich wenn sie nicht debuggen. Wenn Sie sie jemals vollständig bewerten, wäre ich wirklich interessiert, die Ergebnisse zu sehen.
Jon Skeet
3

Nur um meine eigenen jüngsten Erfahrungen zu dieser Diskussion hinzuzufügen: In Übereinstimmung mit den meisten der oben beschriebenen Punkte stellte ich fest, dass das Auslösen von Ausnahmen bei wiederholter Ausführung extrem langsam ist, auch ohne dass der Debugger ausgeführt wird. Ich habe gerade die Leistung eines großen Programms, das ich schreibe, um 60% gesteigert, indem ich ungefähr fünf Codezeilen geändert habe: Wechseln zu einem Rückkehrcode-Modell, anstatt Ausnahmen auszulösen. Zugegeben, der fragliche Code wurde tausende Male ausgeführt und hat möglicherweise Tausende von Ausnahmen ausgelöst, bevor ich ihn geändert habe. Daher stimme ich der obigen Aussage zu: Wirf Ausnahmen aus, wenn etwas Wichtiges tatsächlich schief geht, und nicht, um den Anwendungsfluss in "erwarteten" Situationen zu steuern.

Ray Prisament
quelle
2

Wenn Sie sie mit Rückkehrcodes vergleichen, sind sie höllisch langsam. Wie bereits in früheren Postern erwähnt, möchten Sie den normalen Programmbetrieb nicht aktivieren, sodass Sie nur dann den perfekten Treffer erzielen, wenn ein Problem auftritt und in den allermeisten Fällen die Leistung keine Rolle mehr spielt (da die Ausnahme ohnehin eine Straßensperre impliziert).

Es lohnt sich auf jeden Fall, sie gegenüber Fehlercodes zu verwenden. Die Vorteile sind enorme IMO.

Streit
quelle
2

Ich hatte noch nie Leistungsprobleme mit Ausnahmen. Ich verwende häufig Ausnahmen - ich verwende niemals Rückkehrcodes, wenn ich kann. Sie sind eine schlechte Praxis und riechen meiner Meinung nach nach Spaghetti-Code.

Ich denke, es läuft alles darauf hinaus, wie Sie Ausnahmen verwenden: Wenn Sie sie wie Rückkehrcodes verwenden (jeder Methodenaufruf im Stapel fängt und wirft erneut), dann sind sie langsam, weil Sie jeden einzelnen Fang / Wurf über Kopf haben.

Wenn Sie jedoch am unteren Rand des Stapels werfen und am oberen Rand fangen (Sie ersetzen eine ganze Kette von Rückkehrcodes durch einen Wurf / Fang), werden alle kostspieligen Vorgänge einmal ausgeführt.

Letztendlich sind sie eine gültige Sprachfunktion.

Nur um meinen Standpunkt zu beweisen

Bitte führen Sie den Code unter diesem Link aus (zu groß für eine Antwort).

Ergebnisse auf meinem Computer:

marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM

Zeitstempel werden am Anfang zwischen Rückkehrcodes und Ausnahmen am Ende ausgegeben. In beiden Fällen dauert es gleich lange. Beachten Sie, dass Sie mit Optimierungen kompilieren müssen.

Sklivvz
quelle
2

Aber Mono löst eine Ausnahme 10x schneller als der .net-Standalone-Modus aus, und der .net-Standalone-Modus löst eine Ausnahme 60x schneller als der .net-Debugger-Modus aus. (Testmaschinen haben das gleiche CPU-Modell)

int c = 1000000;
int s = Environment.TickCount;
for (int i = 0; i < c; i++)
{
    try { throw new Exception(); }
    catch { }
}
int d = Environment.TickCount - s;

Console.WriteLine(d + "ms / " + c + " exceptions");
linquize
quelle
1

Im Release-Modus ist der Overhead minimal.

Ich bezweifle, dass Sie den Unterschied bemerken werden, es sei denn, Sie werden Ausnahmen für die Flusskontrolle (z. B. nicht lokale Exits) rekursiv verwenden.

Leppie
quelle
1

In der Windows-CLR ist das Auslösen einer Ausnahme für eine Aufrufkette mit einer Tiefe von 8 750-mal langsamer als das Überprüfen und Weitergeben eines Rückgabewerts. (siehe unten für Benchmarks)

Diese hohen Kosten für Ausnahmen sind darauf zurückzuführen, dass die Windows-CLR in die sogenannte strukturierte Ausnahmebehandlung von Windows integriert ist . Auf diese Weise können Ausnahmen ordnungsgemäß abgefangen und über verschiedene Laufzeiten und Sprachen hinweg ausgelöst werden. Es ist jedoch sehr sehr langsam.

Ausnahmen in der Mono-Laufzeit (auf jeder Plattform) sind viel schneller, da sie nicht in SEH integriert sind. Es gibt jedoch einen Funktionsverlust, wenn Ausnahmen über mehrere Laufzeiten hinweg übergeben werden, da nichts wie SEH verwendet wird.

Hier sind die abgekürzten Ergebnisse meines Benchmarks für Ausnahmen und Rückgabewerte für die Windows-CLR.

baseline: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.25 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.5 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 0.75 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 1 (0), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0 (0), time elapsed 13.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.25 (249999), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.5 (499999), time elapsed 16.0009 ms
retval_error: recurse_depth 5, error_freqeuncy 0.75 (999999), time elapsed 16.001 ms
retval_error: recurse_depth 5, error_freqeuncy 1 (999999), time elapsed 16.0009 ms
retval_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 20.0011 ms
retval_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 21.0012 ms
retval_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 24.0013 ms
exception_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 31.0017 ms
exception_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 5607.3208     ms
exception_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 11172.639  ms
exception_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 22297.2753 ms
exception_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 22102.2641 ms

Und hier ist der Code ..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

public class TestIt {
    int value;

    public class TestException : Exception { } 

    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    public bool baseline_null(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            return shouldfail;
        } else {
            return baseline_null(shouldfail,recurse_depth-1);
        }
    }

    public bool retval_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                return false;
            } else {
                return true;
            }
        } else {
            bool nested_error = retval_error(shouldfail,recurse_depth-1);
            if (nested_error) {
                return true;
            } else {
                return false;
            }
        }
    }

    public void exception_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                throw new TestException();
            }
        } else {
            exception_error(shouldfail,recurse_depth-1);
        }

    }

    public static void Main(String[] args) {
        int i;
        long l;
        TestIt t = new TestIt();
        int failures;

        int ITERATION_COUNT = 1000000;


        // (0) baseline null workload
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    t.baseline_null(shoulderror,recurse_depth);
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "baseline: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }


        // (1) retval_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    if (!t.retval_error(shoulderror,recurse_depth)) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "retval_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }

        // (2) exception_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    try {
                        t.exception_error(shoulderror,recurse_depth);
                    } catch (TestException e) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "exception_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));         }
        }
    }
}


}
David Jeske
quelle
5
Verwenden Sie DateTime.Now nicht für Benchmarks, sondern verwenden Sie die Stoppuhr, mit der die verstrichene Zeit gemessen werden kann. Es sollte hier kein Problem sein, da Sie relativ lange Zeiträume messen, aber es lohnt sich, sich daran zu gewöhnen.
Jon Skeet
Im Gegenteil, die Frage ist "Sind Ausnahmen langsam", Punkt. Es wurde ausdrücklich darum gebeten, das Thema zu vermeiden, wann Ausnahmen ausgelöst werden sollen, da dieses Thema die Fakten verschleiert. Was ist die Leistung von Ausnahmen?
David Jeske
0

Ein kurzer Hinweis hier zur Leistung beim Abfangen von Ausnahmen.

Wenn der Ausführungspfad in einen 'Try'-Block eintritt, passiert nichts Magisches. Es gibt keine 'try'-Anweisung und keine Kosten, die mit dem Betreten oder Verlassen des try-Blocks verbunden sind. Informationen zum try-Block werden in den Metadaten der Methode gespeichert, und diese Metadaten werden zur Laufzeit verwendet, wenn eine Ausnahme ausgelöst wird. Die Ausführungs-Engine geht den Stapel entlang und sucht nach dem ersten Aufruf, der in einem try-Block enthalten war. Der mit der Ausnahmebehandlung verbundene Overhead tritt nur auf, wenn Ausnahmen ausgelöst werden.

Drew Noakes
quelle
1
Das Vorhandensein von Ausnahmen kann sich jedoch auf die Optimierung auswirken. Methoden mit expliziten Ausnahmebehandlungsroutinen sind schwieriger zu integrieren, und die Neuordnung von Anweisungen wird durch sie eingeschränkt.
Eamon Nerbonne
-1

Beim Schreiben von Klassen / Funktionen für andere scheint es schwierig zu sein, zu sagen, wann Ausnahmen angemessen sind. Es gibt einige nützliche Teile von BCL, die ich loswerden und Pinvoke machen musste, weil sie Ausnahmen auslösen, anstatt Fehler zurückzugeben. In einigen Fällen können Sie es umgehen, in anderen Fällen wie System.Management und Leistungsindikatoren gibt es Verwendungen, bei denen Sie Schleifen ausführen müssen, in denen Ausnahmen häufig von BCL ausgelöst werden.

Wenn Sie eine Bibliothek schreiben und die Möglichkeit besteht, dass Ihre Funktion in einer Schleife verwendet wird und eine große Anzahl von Iterationen möglich ist, verwenden Sie das Try .. -Muster oder eine andere Methode, um die Fehler neben Ausnahmen anzuzeigen. Und selbst dann ist es schwer zu sagen, wie oft Ihre Funktion aufgerufen wird, wenn sie von vielen Prozessen in einer gemeinsam genutzten Umgebung verwendet wird.

In meinem eigenen Code werden Ausnahmen nur ausgelöst, wenn die Dinge so außergewöhnlich sind, dass es notwendig ist, in der Stapelverfolgung nachzusehen, was schief gelaufen ist, und sie dann zu beheben. Daher habe ich Teile von BCL so gut wie neu geschrieben, um die Fehlerbehandlung basierend auf dem Try .. -Muster anstelle von Ausnahmen zu verwenden.

Anonymer Feigling
quelle
2
Dies scheint nicht zu der Aussage des Posters " Ich möchte keine Diskussion darüber, wann Ausnahmen zu werfen sind und wann nicht " zu passen .
hrbrmstr