Was ist der tatsächliche Aufwand für Try / Catch in C #?

94

Ich weiß also, dass try / catch einen gewissen Overhead verursacht und daher keine gute Möglichkeit ist, den Prozessfluss zu steuern. Aber woher kommt dieser Overhead und wie wirkt er sich tatsächlich aus?

JC Grubbs
quelle

Antworten:

52

Ich bin kein Experte für Sprachimplementierungen (nehmen Sie dies also mit einem Körnchen Salz), aber ich denke, eine der größten Kosten besteht darin, den Stapel abzuwickeln und für die Stapelverfolgung zu speichern. Ich vermute, dass dies nur passiert, wenn die Ausnahme ausgelöst wird (aber ich weiß es nicht), und wenn ja, würde dies jedes Mal, wenn eine Ausnahme ausgelöst wird, eine angemessene Größe versteckter Kosten haben ... es ist also nicht so, als würden Sie nur von einem Ort springen Im Code zu einem anderen ist viel los.

Ich denke nicht, dass es ein Problem ist, solange Sie Ausnahmen für AUSSERGEWÖHNLICHES Verhalten verwenden (also nicht Ihren typischen, erwarteten Pfad durch das Programm).

Mike Stone
quelle
33
Genauer gesagt: versuchen ist billig, fangen ist billig, werfen ist teuer. Wenn Sie es vermeiden, zu versuchen und zu fangen, ist das Werfen immer noch teuer.
Windows-Programmierer
4
Hmmm - Markup funktioniert in Kommentaren nicht. Um es erneut zu versuchen - Ausnahmen gelten für Fehler, nicht für "außergewöhnliches Verhalten" oder Bedingungen: blogs.msdn.com/kcwalina/archive/2008/07/17/…
HTTP 410
2
@ Windows Programmierer Statistiken / Quelle bitte?
Kapé
100

Drei Punkte hier zu machen:

  • Erstens gibt es wenig oder KEINE Leistungseinbußen, wenn Sie tatsächlich Try-Catch-Blöcke in Ihrem Code haben. Dies sollte nicht berücksichtigt werden, wenn Sie vermeiden möchten, dass sie in Ihrer Anwendung enthalten sind. Der Leistungstreffer kommt nur ins Spiel, wenn eine Ausnahme ausgelöst wird.

  • Wenn zusätzlich zu den Stapelabwicklungsvorgängen usw., die von anderen erwähnt wurden, eine Ausnahme ausgelöst wird, sollten Sie sich darüber im Klaren sein, dass eine ganze Reihe von Laufzeit- / Reflexionsvorgängen ausgeführt wird, um die Mitglieder der Ausnahmeklasse wie die Stapelverfolgung zu füllen Objekt und die verschiedenen Typelemente usw.

  • Ich glaube, dass dies einer der Gründe ist, warum der allgemeine Ratschlag, wenn Sie throw;die Ausnahme erneut auslösen möchten, darin besteht , die Ausnahme nicht erneut auszulösen oder eine neue zu erstellen, da in diesen Fällen alle diese Stapelinformationen wiedererlangt werden, während dies im einfachen Fall der Fall ist werfen es ist alles erhalten.

Shaun Austin
quelle
41
Außerdem: Wenn Sie eine Ausnahme als "throw ex" erneut auslösen, verlieren Sie den ursprünglichen Stack-Trace und ersetzen ihn durch den CURRENT-Stack-Trace. selten was gewollt ist. Wenn Sie nur "werfen", bleibt die ursprüngliche Stapelverfolgung in der Ausnahme erhalten.
Eddie
@ Eddie Orthrow new Exception("Wrapping layer’s error", ex);
Binki
21

Fragen Sie nach dem Aufwand für die Verwendung von try / catch / finally, wenn keine Ausnahmen ausgelöst werden, oder nach dem Aufwand für die Verwendung von Ausnahmen zur Steuerung des Prozessflusses? Letzteres ähnelt in gewisser Weise der Verwendung eines Dynamitstabs zum Anzünden der Geburtstagskerze eines Kleinkindes, und der damit verbundene Aufwand fällt in die folgenden Bereiche:

  • Sie können zusätzliche Cache-Fehler erwarten, da die Ausnahme auf residente Daten zugreift, die sich normalerweise nicht im Cache befinden.
  • Sie können zusätzliche Seitenfehler erwarten, da die Ausnahme auf nicht residenten Code und Daten zugreift, die normalerweise nicht im Arbeitssatz Ihrer Anwendung enthalten sind.

    • Zum Auslösen der Ausnahme muss die CLR beispielsweise den Speicherort der Blöcke finally und catch anhand der aktuellen IP und der Rückgabe-IP jedes Frames ermitteln, bis die Ausnahme plus Filterblock behandelt wird.
    • Zusätzliche Baukosten und Namensauflösung, um die Frames für Diagnosezwecke zu erstellen, einschließlich Lesen von Metadaten usw.
    • Beide oben genannten Elemente greifen normalerweise auf "kalten" Code und Daten zu, sodass harte Seitenfehler wahrscheinlich sind, wenn Sie überhaupt Speicherdruck haben:

      • Die CLR versucht, Code und Daten, die selten verwendet werden, weit entfernt von Daten zu platzieren, die häufig zur Verbesserung der Lokalität verwendet werden. Dies wirkt sich also gegen Sie aus, da Sie die Kälte dazu zwingen, heiß zu sein.
      • Die Kosten für die Fehler auf der harten Seite, falls vorhanden, werden alles andere in den Schatten stellen.
  • Typische Fangsituationen sind oft tiefgreifend, daher werden die oben genannten Effekte tendenziell verstärkt (was die Wahrscheinlichkeit von Seitenfehlern erhöht).

Die tatsächlichen Auswirkungen der Kosten können sehr unterschiedlich sein, je nachdem, was zu diesem Zeitpunkt noch in Ihrem Code vor sich geht. Jon Skeet hat hier eine gute Zusammenfassung mit einigen nützlichen Links. Ich stimme seiner Aussage eher zu, dass Sie Probleme mit der Verwendung von Ausnahmen haben, die über die Leistung hinausgehen, wenn Sie an einem Punkt angelangt sind, an dem Ausnahmen Ihre Leistung erheblich beeinträchtigen.

HTTP 410
quelle
6

Nach meiner Erfahrung besteht der größte Aufwand darin, eine Ausnahme auszulösen und damit umzugehen. Ich habe einmal an einem Projekt gearbeitet, bei dem Code ähnlich dem folgenden verwendet wurde, um zu überprüfen, ob jemand das Recht hatte, ein Objekt zu bearbeiten. Diese HasRight () -Methode wurde überall in der Präsentationsebene verwendet und wurde häufig für Hunderte von Objekten aufgerufen.

bool HasRight(string rightName, DomainObject obj) {
  try {
    CheckRight(rightName, obj);
    return true;
  }
  catch (Exception ex) {
    return false;
  }
}

void CheckRight(string rightName, DomainObject obj) {
  if (!_user.Rights.Contains(rightName))
    throw new Exception();
}

Wenn die Testdatenbank mit Testdaten voller wurde, führte dies zu einer sehr sichtbaren Verlangsamung beim Öffnen neuer Formulare usw.

Also habe ich es auf Folgendes umgestaltet, was - nach späteren schnellen und schmutzigen Messungen - etwa 2 Größenordnungen schneller ist:

bool HasRight(string rightName, DomainObject obj) {
  return _user.Rights.Contains(rightName);
}

void CheckRight(string rightName, DomainObject obj) {
  if (!HasRight(rightName, obj))
    throw new Exception();
}

Kurz gesagt, die Verwendung von Ausnahmen im normalen Prozessfluss ist etwa zwei Größenordnungen langsamer als die Verwendung eines ähnlichen Prozessflusses ohne Ausnahmen.

Tobi
quelle
1
Warum sollten Sie hier eine Ausnahme auslösen wollen? Sie könnten den Fall behandeln, dass Sie die Rechte nicht vor Ort haben.
ThunderGr
@ThunderGr das habe ich eigentlich geändert und es um zwei Größenordnungen schneller gemacht.
Tobi
5

Im Gegensatz zu allgemein akzeptierten Theorien kann try/ catcherhebliche Auswirkungen auf die Leistung haben, und das ist, ob eine Ausnahme ausgelöst wird oder nicht!

  1. Es deaktiviert einige automatische Optimierungen (vom Design her) und fügt in einigen Fällen Debugging- Code ein, wie Sie es von einer Debugging-Hilfe erwarten können . Es wird immer Leute geben, die mir in diesem Punkt nicht zustimmen, aber die Sprache erfordert es und die Demontage zeigt es, so dass diese Leute per Wörterbuchdefinition wahnhaft sind .
  2. Dies kann sich negativ auf die Wartung auswirken. Dies ist hier tatsächlich das wichtigste Problem, aber da meine letzte Antwort (die sich fast ausschließlich darauf konzentrierte) gelöscht wurde, werde ich versuchen, mich auf das weniger wichtige Problem (die Mikrooptimierung) zu konzentrieren, im Gegensatz auf das wichtigere Problem ( die Makrooptimierung).

Ersteres wurde im Laufe der Jahre in einigen Blog-Posts von Microsoft MVPs behandelt, und ich vertraue darauf, dass Sie sie leicht finden konnten, doch StackOverflow kümmert sich so sehr um den Inhalt, dass ich Links zu einigen von ihnen als Füllnachweis bereitstellen werde :

Es gibt auch diese Antwort, die den Unterschied zwischen zerlegtem Code mit und ohne try/ zeigt catch.

Es scheint so offensichtlich , dass es ist ein Overhead , die offensichtlich beobachtbar in der Codegenerierung ist, und das Overhead scheint auch von Menschen , die Microsoft - Wert anerkannt zu werden! Dennoch wiederhole ich das Internet ...

Ja, es gibt Dutzende zusätzlicher MSIL-Anweisungen für eine triviale Codezeile, und das deckt nicht einmal die deaktivierten Optimierungen ab, so dass es sich technisch gesehen um eine Mikrooptimierung handelt.


Ich habe vor Jahren eine Antwort gepostet, die gelöscht wurde, da sie sich auf die Produktivität von Programmierern konzentrierte (die Makrooptimierung).

Dies ist bedauerlich, da hier und da keine Einsparung von wenigen Nanosekunden CPU-Zeit wahrscheinlich viele Stunden manueller Optimierung durch den Menschen ausgleichen wird. Wofür bezahlt Ihr Chef mehr: eine Stunde Ihrer Zeit oder eine Stunde bei laufendem Computer? Ab wann ziehen wir den Stecker und geben zu, dass es Zeit ist, nur einen schnelleren Computer zu kaufen ?

Natürlich sollten wir unsere Prioritäten optimieren , nicht nur unseren Code! In meiner letzten Antwort habe ich auf die Unterschiede zwischen zwei Codeausschnitten zurückgegriffen.

Verwenden von try/ catch:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

Nicht verwenden try/ catch:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

Betrachten Sie aus der Sicht eines Wartungsentwicklers, der Ihre Zeit eher verschwendet, wenn nicht bei der Profilerstellung / Optimierung (siehe oben), die ohne das try/ catch-Problem wahrscheinlich nicht einmal notwendig wäre , dann beim Scrollen durch Quellcode ... Eine davon hat vier zusätzliche Zeilen Müll auf dem Heizkessel!

Da immer mehr Felder in eine Klasse eingeführt werden, sammelt sich der gesamte Müll auf der Kesselplatte (sowohl im Quellcode als auch im zerlegten Code) weit über vernünftige Werte hinaus. Vier zusätzliche Zeilen pro Feld, und sie sind immer die gleichen Zeilen ... Wurde uns nicht beigebracht, uns nicht zu wiederholen? Ich nehme an, wir könnten das try/ catchhinter einer selbst gebrauten Abstraktion verstecken , aber ... dann könnten wir genauso gut Ausnahmen vermeiden (dh verwenden Int.TryParse).

Dies ist nicht einmal ein komplexes Beispiel; Ich habe Versuche gesehen, neue Klassen in try/ zu instanziieren catch. Beachten Sie, dass der gesamte Code im Konstruktor möglicherweise von bestimmten Optimierungen ausgeschlossen wird, die sonst vom Compiler automatisch angewendet würden. Welchen besseren Weg gibt es, um die Theorie zu begründen, dass der Compiler langsam ist , im Gegensatz zum Compiler, der genau das tut, was ihm gesagt wurde ?

Unter der Annahme, dass der Konstruktor eine Ausnahme auslöst und dadurch ein Fehler ausgelöst wird, muss der schlechte Wartungsentwickler diese aufspüren. Das mag nicht so eine einfache Aufgabe sein, da im Gegensatz zu dem Spaghetti - Code des goto Alptraums, try/ catchkönnen Verwirrungen in verursacht drei Dimensionen , wie sie den Stapel in nicht nur anderen Teile der gleichen Methode bewegen können, aber auch andere Klassen und Methoden , die alle vom Wartungsentwickler auf die harte Tour beobachtet werden ! Dennoch wird uns gesagt, dass "goto ist gefährlich", heh!

Am Ende erwähne ich, try/ catchhat seinen Vorteil, dass es Optimierungen deaktivieren soll ! Es ist, wenn Sie so wollen, eine Debugging-Hilfe ! Dafür wurde es entwickelt und es sollte als ...

Ich denke, das ist auch ein positiver Punkt. Es kann verwendet werden, um Optimierungen zu deaktivieren, die andernfalls sichere, vernünftige Algorithmen für die Nachrichtenübermittlung für Multithread-Anwendungen lähmen könnten, und um mögliche Rennbedingungen zu erfassen;) Dies ist ungefähr das einzige Szenario, das ich mir vorstellen kann, try / catch zu verwenden. Auch das hat Alternativen.


Welche Optimierungen tun try, catchund finallydeaktivieren?

AKA

Wie sind try, catchund finallynützlich als Hilfsmittel Debugging?

Sie sind Schreibbarrieren. Dies kommt aus dem Standard:

12.3.3.13 Try-Catch-Anweisungen

Für eine Anweisung stmt des Formulars:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
  • Der definitive Zuweisungsstatus von v zu Beginn des Try-Blocks ist der gleiche wie der definitive Zuweisungsstatus von v zu Beginn von stmt .
  • Der definitive Zuweisungsstatus von v zu Beginn von catch-block-i (für jedes i ) ist der gleiche wie der definitive Zuweisungsstatus von v zu Beginn von stmt .
  • Der definitive Zuweisungsstatus von v am Endpunkt von stmt wird definitiv zugewiesen, wenn (und nur wenn) v definitiv am Endpunkt von try-block und jedem catch-Block-i zugewiesen wird (für jedes i von 1 bis n ).

Mit anderen Worten, am Anfang jeder tryAussage:

  • Alle Zuweisungen an sichtbare Objekte vor der Eingabe der tryAnweisung müssen abgeschlossen sein. Für den Start ist eine Thread-Sperre erforderlich, die sich zum Debuggen von Rennbedingungen eignet.
  • Der Compiler darf nicht:
    • Entfernen Sie nicht verwendete Variablenzuweisungen, die definitiv vor der tryAnweisung zugewiesen wurden
    • Reorganisieren oder verschmelzen Sie eine der inneren Aufgaben (siehe meinen ersten Link, falls Sie dies noch nicht getan haben).
    • Hebezuweisungen über diese Barriere, um die Zuordnung zu einer Variablen zu verzögern, von der bekannt ist, dass sie erst später (wenn überhaupt) verwendet wird, oder um spätere Zuweisungen vorbeugend vorwärts zu verschieben, um andere Optimierungen zu ermöglichen ...

Eine ähnliche Geschichte gilt für jede catchAussage; Angenommen try, Sie weisen Ihrer Anweisung (oder einem Konstruktor oder einer Funktion, die sie aufruft, usw.), die Sie dieser ansonsten sinnlosen Variablen zuweisen (sagen wir, garbage=42;) zu, diese Anweisung nicht zu entfernen, unabhängig davon, wie irrelevant sie für das beobachtbare Verhalten des Programms ist . Die Zuordnung muss abgeschlossen sein, bevor der catchBlock eingegeben wird.

Für das, was es wert ist, finallyerzählt eine ähnlich erniedrigende Geschichte:

12.3.3.14 Try-finally-Anweisungen

Für eine try- Anweisung stmt des Formulars:

try try-block
finally finally-block

• Der definitive Zuweisungsstatus von v zu Beginn des Try-Blocks ist der gleiche wie der definitive Zuweisungsstatus von v zu Beginn von stmt .
• Der definitive Zuweisungsstatus von v zu Beginn des finally-Blocks ist der gleiche wie der definitive Zuweisungsstatus von v zu Beginn von stmt .
• Der definitive Zuweisungsstatus von v am Endpunkt von stmt wird definitiv zugewiesen, wenn (und nur wenn): o v wird definitiv am Endpunkt von try-block o v zugewiesenwird definitiv am Endpunkt des finally-Blocks zugewiesen. Wenn eine Kontrollflussübertragung (z. B. eine goto- Anweisung) durchgeführt wird, die innerhalb des try-Blocks beginnt und außerhalb des try-Blocks endet , wird v auch als definitiv zugewiesen betrachtet der Steuerfluss , wenn Übertragungs v definitiv am Endpunkt zugeordnet ist schließlich Block . (Dies ist nicht nur dann der Fall, wenn - wenn v aus einem anderen Grund bei dieser Kontrollflussübertragung definitiv zugewiesen ist, es immer noch als definitiv zugewiesen gilt.)

12.3.3.15 Try-catch-finally-Anweisungen

Definitive Zuordnungsanalyse für eine try - catch - finally - Anweisung des Formulars:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
finally finally-block

getan wird , als ob die Aussage were a try - schließlich Anweisung eine umschließende try - catch - Anweisung:

try { 
    try   
    try-block
    catch ( ... ) catch-block-1
    ...   
    catch ( ... ) catch-block-n
} 
finally finally-block
autistisch
quelle
3

Ganz zu schweigen davon, dass sich eine häufig aufgerufene Methode auf das Gesamtverhalten der Anwendung auswirken kann.
Zum Beispiel betrachte ich die Verwendung von Int32.Parse in den meisten Fällen als eine schlechte Praxis, da es Ausnahmen für etwas auslöst, das sonst leicht abgefangen werden kann.


Um alles zu schließen, was hier geschrieben steht: 1) Verwenden Sie try..catch-Blöcke, um unerwartete Fehler abzufangen - fast keine Leistungseinbußen.
2) Verwenden Sie keine Ausnahmen für ausgenommene Fehler, wenn Sie dies vermeiden können.

dotmad
quelle
3

Ich habe vor einiger Zeit einen Artikel darüber geschrieben, weil zu dieser Zeit viele Leute danach fragten. Sie finden es und den Testcode unter http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx .

Das Ergebnis ist, dass ein Try / Catch-Block nur einen geringen Overhead hat, aber so klein ist, dass er ignoriert werden sollte. Wenn Sie jedoch Try / Catch-Blöcke in Schleifen ausführen, die millionenfach ausgeführt werden, sollten Sie den Block nach Möglichkeit außerhalb der Schleife verschieben.

Das Hauptleistungsproblem bei Try / Catch-Blöcken besteht darin, dass Sie tatsächlich eine Ausnahme abfangen. Dies kann zu einer spürbaren Verzögerung Ihrer Anwendung führen. Wenn etwas schief geht, erkennen die meisten Entwickler (und viele Benutzer) die Pause natürlich als eine Ausnahme, die bald eintreten wird! Der Schlüssel hier ist, die Ausnahmebehandlung nicht für den normalen Betrieb zu verwenden. Wie der Name schon sagt, sind sie außergewöhnlich und Sie sollten alles tun, um zu verhindern, dass sie geworfen werden. Sie sollten sie nicht als Teil des erwarteten Ablaufs eines Programms verwenden, das ordnungsgemäß funktioniert.

BlackWasp
quelle
2

Ich habe letztes Jahr einen Blogeintrag zu diesem Thema gemacht. Hör zu. Fazit ist, dass für einen Try-Block fast keine Kosten anfallen, wenn keine Ausnahme auftritt - und auf meinem Laptop lag eine Ausnahme bei etwa 36 μs. Das ist vielleicht weniger als erwartet, aber denken Sie daran, dass diese Ergebnisse auf einem flachen Stapel liegen. Auch die ersten Ausnahmen sind sehr langsam.

Hafthor
quelle
Ich konnte Ihr Blog nicht erreichen (die Verbindung läuft ab; verwenden Sie try/ catchzu viel? Heh heh), aber Sie scheinen mit der Sprachspezifikation und einigen MS-MVPs zu streiten, die auch Blogs zu diesem Thema geschrieben haben und Messungen bereitstellen im Gegenteil zu Ihrem Rat ... Ich bin offen für den Vorschlag, dass die von mir durchgeführten Recherchen falsch sind, aber ich muss Ihren Blogeintrag lesen, um zu sehen, was darin steht.
autistisch
Neben dem Blog-Beitrag von @ Hafthor gibt es hier einen weiteren Blog-Beitrag mit Code, der speziell zum Testen von Geschwindigkeitsleistungsunterschieden geschrieben wurde. Laut den Ergebnissen wird der Ausnahmebehandlungscode insgesamt 100-mal langsamer ausgeführt als der Code für die Nicht-Ausnahmebehandlung, wenn in nur 5% der Fälle eine Ausnahme auftritt. Der Artikel zielt speziell auf den try-catchBlock gegen tryparse()Methoden ab, aber das Konzept ist das gleiche.
2

Es ist wesentlich einfacher, Code zu schreiben, zu debuggen und zu verwalten, der frei von Compiler-Fehlermeldungen, Code-Analyse-Warnmeldungen und routinemäßig akzeptierten Ausnahmen ist (insbesondere Ausnahmen, die an einer Stelle ausgelöst und an einer anderen akzeptiert werden). Weil es einfacher ist, wird der Code im Durchschnitt besser geschrieben und weniger fehlerhaft sein.

Für mich ist dieser Programmierer- und Qualitätsaufwand das Hauptargument gegen die Verwendung von try-catch für den Prozessfluss.

Der Computeraufwand für Ausnahmen ist im Vergleich unbedeutend und im Hinblick auf die Fähigkeit der Anwendung, die tatsächlichen Leistungsanforderungen zu erfüllen, normalerweise gering.


quelle
@Ritchard T, warum? Im Vergleich zum Programmierer und zum Qualitätsaufwand ist er unbedeutend.
ThunderGr
1

Ich mag Hafthors Blog-Post sehr , und um meine zwei Cent zu dieser Diskussion hinzuzufügen, möchte ich sagen, dass es für mich immer einfach war, dass die DATENSCHICHT nur eine Art von Ausnahme auslöst (DataAccessException). Auf diese Weise weiß meine BUSINESS LAYER, welche Ausnahme zu erwarten ist, und fängt sie auf. Abhängig von weiteren Geschäftsregeln (dh wenn mein Geschäftsobjekt am Workflow teilnimmt usw.) kann ich dann eine neue Ausnahme auslösen (BusinessObjectException) oder fortfahren, ohne erneut zu werfen.

Ich würde sagen, zögern Sie nicht, try..catch zu verwenden, wann immer es notwendig ist, und verwenden Sie es mit Bedacht!

Diese Methode nimmt beispielsweise an einem Workflow teil ...

Bemerkungen?

public bool DeleteGallery(int id)
{
    try
    {
        using (var transaction = new DbTransactionManager())
        {
            try
            {
                transaction.BeginTransaction();

                _galleryRepository.DeleteGallery(id, transaction);
                _galleryRepository.DeletePictures(id, transaction);

                FileManager.DeleteAll(id);

                transaction.Commit();
            }
            catch (DataAccessException ex)
            {
                Logger.Log(ex);
                transaction.Rollback();                        
                throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
            }
        }
    }
    catch (DbTransactionException ex)
    {
        Logger.Log(ex);
        throw new BusinessObjectException("Cannot delete gallery.", ex);
    }
    return true;
}

quelle
2
David, würden Sie den Aufruf von 'DeleteGallery' in einen Try / Catch-Block einschließen?
Rob
Da die DeleteGallery eine boolesche Funktion ist, scheint es mir nicht sinnvoll, dort eine Ausnahme auszulösen. Dies würde erfordern, dass der Aufruf von DeleteGallery in einem Try / Catch-Block eingeschlossen ist. Ein if (! DeleteGallery (theid)) {// handle} sieht für mich aussagekräftiger aus. in diesem speziellen Beispiel.
ThunderGr
1

In Programming Languages ​​Pragmatics von Michael L. Scott können wir lesen, dass die heutigen Compiler im allgemeinen Fall keinen Overhead hinzufügen, dh wenn keine Ausnahmen auftreten. So wird jede Arbeit in Kompilierungszeit gemacht. Wenn jedoch zur Laufzeit eine Ausnahme ausgelöst wird, muss der Compiler eine binäre Suche durchführen, um die richtige Ausnahme zu finden. Dies geschieht für jeden neuen Wurf, den Sie ausgeführt haben.

Aber Ausnahmen sind Ausnahmen und diese Kosten sind durchaus akzeptabel. Wenn Sie versuchen, die Ausnahmebehandlung ohne Ausnahmen durchzuführen und stattdessen Rückgabefehlercodes verwenden, benötigen Sie wahrscheinlich eine if-Anweisung für jede Unterroutine, und dies führt zu einem echten Echtzeit-Overhead. Sie wissen, dass eine if-Anweisung in einige Assembly-Anweisungen konvertiert wird, die jedes Mal ausgeführt werden, wenn Sie in Ihre Unterroutinen eingeben.

Entschuldigung für mein Englisch, hoffe es hilft dir. Diese Informationen basieren auf dem zitierten Buch. Weitere Informationen finden Sie in Kapitel 8.5 Ausnahmebehandlung.

Vando
quelle
Der Compiler ist zur Laufzeit nicht im Bild. Es muss einen Overhead für Try / Catch-Blöcke geben, damit die CLR die Ausnahmen verarbeiten kann. C # wird auf der .NET CLR (einer virtuellen Maschine) ausgeführt. Es scheint mir, dass der Overhead des Blocks selbst minimal ist, wenn es keine Ausnahme gibt, aber die Kosten für die CLR, die die Ausnahme behandelt, sehr hoch sind.
ThunderGr
-4

Lassen Sie uns eine der größtmöglichen Kosten eines Try / Catch-Blocks analysieren, wenn er dort verwendet wird, wo er nicht verwendet werden sollte:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

Und hier ist der ohne Versuch / Fang:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

Ohne Berücksichtigung des unbedeutenden Leerraums könnte man feststellen, dass diese beiden äquivalenten Codeteile in Bytes fast genau dieselbe Länge haben. Letzteres enthält 4 Bytes weniger Einzug. Ist das etwas schlechtes?

Um die Verletzung zusätzlich zu beleidigen, entscheidet sich ein Schüler für eine Schleife, während die Eingabe als int analysiert werden kann. Die Lösung ohne try / catch könnte etwa so aussehen:

while (int.TryParse(...))
{
    ...
}

Aber wie sieht das bei der Verwendung von try / catch aus?

try {
    for (;;)
    {
        x = int.Parse(...);
        ...
    }
}
catch
{
    ...
}

Try / Catch-Blöcke sind magische Methoden, um Einrückungen zu verschwenden, und wir wissen immer noch nicht einmal, warum sie fehlgeschlagen sind! Stellen Sie sich vor, wie sich die Person beim Debuggen fühlt, wenn Code nach einem schwerwiegenden logischen Fehler weiter ausgeführt wird, anstatt mit einem netten offensichtlichen Ausnahmefehler anzuhalten. Try / Catch-Blöcke sind die Datenvalidierung / -hygiene eines faulen Mannes.

Eine der geringeren Kosten besteht darin, dass Try / Catch-Blöcke tatsächlich bestimmte Optimierungen deaktivieren: http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx . Ich denke, das ist auch ein positiver Punkt. Es kann verwendet werden, um Optimierungen zu deaktivieren, die andernfalls sichere, vernünftige Algorithmen für die Nachrichtenübermittlung für Multithread-Anwendungen lähmen könnten, und um mögliche Rennbedingungen zu erfassen;) Dies ist ungefähr das einzige Szenario, das ich mir vorstellen kann, try / catch zu verwenden. Auch das hat Alternativen.

Sebastian Ramadan
quelle
1
Ich bin mir ziemlich sicher, dass TryParse einen Versuch ausführt {int x = int.Parse ("xxx"); return true;} catch {return false; } im Inneren. Einrückung ist kein Problem in der Frage, nur Leistung und Overhead.
ThunderGr
@ThunderGr Alternativ können Sie die neue Antwort lesen, die ich gepostet habe. Es enthält mehr Links, von denen eine Analyse der massiven Leistungssteigerung ist , wenn Sie es vermeiden Int.Parsefür Int.TryParse.
Autist