Beeinträchtigen Try / Catch-Blöcke die Leistung, wenn keine Ausnahmen ausgelöst werden?

274

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?

Kobi
quelle
147
"Wer Korrektheit für Leistung opfern würde, verdient beides nicht."
Joel Coehoorn
16
Die Korrektheit muss jedoch nicht immer für die Leistung geopfert werden.
Dan Davies Brackett
19
Wie wäre es mit einfacher Neugier?
Samantha Branham
63
@ Joel: Vielleicht will Kobi die Antwort nur aus Neugier wissen. Zu wissen, ob die Leistung besser oder schlechter sein wird, bedeutet nicht unbedingt, dass er mit seinem Code etwas Verrücktes machen wird. Ist das Streben nach Wissen nicht um seiner selbst willen eine gute Sache?
LukeH
6
Hier ist ein guter Algorithmus, um zu wissen, ob diese Änderung vorgenommen werden soll oder nicht. Legen Sie zunächst aussagekräftige kundenbasierte Leistungsziele fest. Zweitens schreiben Sie den Code zuerst so, dass er korrekt und klar ist. Drittens testen Sie es gegen Ihre Ziele. Viertens, wenn Sie Ihre Ziele erreichen, brechen Sie die Arbeit frühzeitig ab und gehen Sie zum Strand. Fünftens: Wenn Sie Ihre Ziele nicht erreichen, verwenden Sie einen Profiler, um den zu langsamen Code zu finden. Sechstens: Wenn dieser Code aufgrund eines unnötigen Ausnahmebehandlers zu langsam ist, entfernen Sie erst dann den Ausnahmebehandler. Wenn nicht, korrigieren Sie den Code, der tatsächlich zu langsam ist. Fahren Sie dann mit Schritt drei fort.
Eric Lippert

Antworten:

203

Prüfen Sie.

static public void Main(string[] args)
{
    Stopwatch w = new Stopwatch();
    double d = 0;

    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(1);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    w.Stop();
    Console.WriteLine(w.Elapsed);
    w.Reset();
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(1);
    }

    w.Stop();
    Console.WriteLine(w.Elapsed);
}

Ausgabe:

00:00:00.4269033  // with try/catch
00:00:00.4260383  // without.

In Millisekunden:

449
416

Neuer Code:

for (int j = 0; j < 10; j++)
{
    Stopwatch w = new Stopwatch();
    double d = 0;
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(d);
        }

        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        finally
        {
            d = Math.Sin(d);
        }
    }

    w.Stop();
    Console.Write("   try/catch/finally: ");
    Console.WriteLine(w.ElapsedMilliseconds);
    w.Reset();
    d = 0;
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(d);
        d = Math.Sin(d);
    }

    w.Stop();
    Console.Write("No try/catch/finally: ");
    Console.WriteLine(w.ElapsedMilliseconds);
    Console.WriteLine();
}

Neue Ergebnisse:

   try/catch/finally: 382
No try/catch/finally: 332

   try/catch/finally: 375
No try/catch/finally: 332

   try/catch/finally: 376
No try/catch/finally: 333

   try/catch/finally: 375
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 329

   try/catch/finally: 373
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 352

   try/catch/finally: 374
No try/catch/finally: 331

   try/catch/finally: 380
No try/catch/finally: 329

   try/catch/finally: 374
No try/catch/finally: 334
Ben M.
quelle
24
Können Sie sie auch in umgekehrter Reihenfolge ausprobieren, um sicherzustellen, dass die JIT-Kompilierung keine Auswirkungen auf die erstere hat?
JoshJordan
28
Programme wie dieses scheinen kaum gute Kandidaten für das Testen der Auswirkungen der Ausnahmebehandlung zu sein. Zu viel von dem, was in normalen try {} catch {} -Blöcken passieren würde, wird optimiert. Ich bin vielleicht zum Mittagessen
unterwegs
30
Dies ist ein Debug-Build. Die JIT optimiert diese nicht.
Ben M
7
Dies ist überhaupt nicht wahr, denken Sie daran. Wie oft verwenden Sie try catch in einer Schleife? Die meiste Zeit werden Sie Schleife in einem try.c
Athiwat Chunlakhan
9
"Ja wirklich?" "Wie wirken sich Try / Catch-Blöcke auf die Leistung aus, wenn keine Ausnahmen ausgelöst werden?"
Ben M
105

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 #:

private static void TestWithoutTryCatch(){
    Console.WriteLine("SIN(1) = {0} - No Try/Catch", Math.Sin(1)); 
}

MSIL:

.method private hidebysig static void  TestWithoutTryCatch() cil managed
{
  // Code size       32 (0x20)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "SIN(1) = {0} - No Try/Catch"
  IL_0006:  ldc.r8     1.
  IL_000f:  call       float64 [mscorlib]System.Math::Sin(float64)
  IL_0014:  box        [mscorlib]System.Double
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_001e:  nop
  IL_001f:  ret
} // end of method Program::TestWithoutTryCatch

C #:

private static void TestWithTryCatch(){
    try{
        Console.WriteLine("SIN(1) = {0}", Math.Sin(1)); 
    }
    catch (Exception ex){
        Console.WriteLine(ex);
    }
}

MSIL:

.method private hidebysig static void  TestWithTryCatch() cil managed
{
  // Code size       49 (0x31)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Exception ex)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "SIN(1) = {0}"
    IL_0007:  ldc.r8     1.
    IL_0010:  call       float64 [mscorlib]System.Math::Sin(float64)
    IL_0015:  box        [mscorlib]System.Double
    IL_001a:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                  object)
    IL_001f:  nop
    IL_0020:  nop
    IL_0021:  leave.s    IL_002f //JUMP IF NO EXCEPTION
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0023:  stloc.0
    IL_0024:  nop
    IL_0025:  ldloc.0
    IL_0026:  call       void [mscorlib]System.Console::WriteLine(object)
    IL_002b:  nop
    IL_002c:  nop
    IL_002d:  leave.s    IL_002f
  }  // end handler
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::TestWithTryCatch

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 siebzehn IL_0021: leave.s IL_002f. Wenn eine Ausnahme auftritt, springt die Steuerung zur Zeile, IL_0025: ldloc.0andernfalls springen wir zur Bezeichnung IL_002d: leave.s IL_002fund 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 nur und ein Sprungbefehl.

TheVillageIdiot
quelle
33
Nun, die IL enthält einen Try / Catch-Block in der gleichen Notation wie in C #. Dies zeigt also nicht wirklich, wie viel Overhead ein Try / Catch hinter den Kulissen bedeutet! Nur dass die IL nicht viel mehr hinzufügt, bedeutet nicht dasselbe, da dem kompilierten Assemblycode nichts hinzugefügt wird. Die IL ist nur eine gemeinsame Darstellung aller .NET-Sprachen. Es ist KEIN Maschinencode!
Ehrfurcht
64

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.

John Kugelman
quelle
10
Das ist ein ausgezeichneter Punkt - im Vergleich zu den anderen Elementen auf unserer Liste sollte dieser winzig sein. Wir sollten darauf vertrauen, dass sich grundlegende Sprachfunktionen korrekt verhalten, und optimieren, was wir steuern können (SQL, Indizes, Algorithmen).
Kobi
3
Denken Sie an enge Schleifen Kumpel. Beispiel: Die Schleife, in der Sie Objekte aus einem Socket-Datenstrom im Spielserver lesen und de-serialisieren und versuchen, so viel wie möglich zu komprimieren. Sie verwenden also MessagePack für die Objektserialisierung anstelle von Binärformatierern und verwenden ArrayPool <Byte>, anstatt nur Byte-Arrays usw. zu erstellen. In diesen Szenarien wirken sich mehrere (möglicherweise verschachtelte) Catch-Blöcke innerhalb der engen Schleife aus. Einige Optimierungen werden vom Compiler übersprungen, auch die Ausnahmevariable geht an Gen0 GC. Ich sage nur, dass es "einige" Szenarien gibt, in denen sich alles auswirkt.
Tcwicks
35

Ziemlich umfassende Erklärung des .NET-Ausnahmemodells.

Rico Marianis Performance-Leckerbissen: Ausnahmekosten: Wann werfen und wann nicht?

Die erste Art von Kosten sind die statischen Kosten für die Ausnahmebehandlung in Ihrem Code. Verwaltete Ausnahmen sind hier tatsächlich vergleichsweise gut, womit ich meine, dass die statischen Kosten viel niedriger sein können als beispielsweise in C ++. Warum ist das? Nun, statische Kosten entstehen wirklich an zwei Arten von Orten: Erstens an den tatsächlichen Sites von try / finally / catch / throw, an denen Code für diese Konstrukte vorhanden ist. Zweitens entstehen in unverändertem Code die Stealth-Kosten, die mit der Verfolgung aller Objekte verbunden sind, die für den Fall, dass eine Ausnahme ausgelöst wird, zerstört werden müssen. Es muss eine beträchtliche Menge an Bereinigungslogik vorhanden sein, und der hinterhältige Teil ist, dass selbst Code dies nicht tut.

Dmitriy Zaslavskiy:

Laut Chris Brummes Anmerkung: Es gibt auch Kosten im Zusammenhang mit der Tatsache, dass einige Optimierungen von JIT nicht durchgeführt werden, wenn Fang vorhanden ist

arul
quelle
1
Was C ++ betrifft, ist, dass ein sehr großer Teil der Standardbibliothek Ausnahmen auslöst. Es gibt nichts Optionales an ihnen. Sie müssen Ihre Objekte mit einer Art Ausnahmerichtlinie entwerfen, und wenn Sie dies getan haben, fallen keine Stealth-Kosten mehr an.
David Thornley
Rico Marianis Behauptungen sind für natives C ++ völlig falsch. "Die statischen Kosten können viel niedriger sein als beispielsweise in C ++" - Dies ist einfach nicht wahr. Ich bin mir jedoch nicht sicher, wie der Ausnahmemechanismus 2003 entworfen wurde, als der Artikel geschrieben wurde. C ++ wirklich keine Kosten überhaupt , wenn Ausnahmen werden nicht geworfen, egal wie viele try / catch - Blöcke Sie haben und wo sie sind.
BJovke
1
@BJovke C ++ "Null-Kosten-Ausnahmebehandlung" bedeutet nur, dass keine Laufzeitkosten anfallen, wenn keine Ausnahmen ausgelöst werden. Aufgrund des gesamten Bereinigungscodes, der Destruktoren für Ausnahmen aufruft, fallen jedoch immer noch erhebliche Kosten für die Codegröße an. Auch wenn auf dem normalen Codepfad kein ausnahmespezifischer Code generiert wird, sind die Kosten immer noch nicht Null, da die Möglichkeit von Ausnahmen den Optimierer immer noch einschränkt (z. B. müssen Dinge, die im Falle einer Ausnahme benötigt werden, erhalten bleiben irgendwo herum -> Werte können weniger aggressiv verworfen werden -> weniger effiziente Registerzuordnung)
Daniel
24

Die Struktur unterscheidet sich in dem Beispiel von Ben M . Es wird über Kopf innerhalb der inneren forSchleife 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:

        for (int j = 0; j < 10; j++)
        {
            Stopwatch w = new Stopwatch();
            w.Start();
            try { 
                double d1 = 0; 
                for (int i = 0; i < 10000000; i++) { 
                    d1 = Math.Sin(d1);
                    d1 = Math.Sin(d1); 
                } 
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString()); 
            }
            finally { 
                //d1 = Math.Sin(d1); 
            }
            w.Stop(); 
            Console.Write("   try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            w.Reset(); 
            w.Start(); 
            double d2 = 0; 
            for (int i = 0; i < 10000000; i++) { 
                d2 = Math.Sin(d2);
                d2 = Math.Sin(d2); 
            } 
            w.Stop(); 
            Console.Write("No try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            Console.WriteLine();
        }

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:

   try/catch/finally: 509
No try/catch/finally: 486

   try/catch/finally: 479
No try/catch/finally: 511

   try/catch/finally: 475
No try/catch/finally: 477

   try/catch/finally: 477
No try/catch/finally: 475

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 477
No try/catch/finally: 474

   try/catch/finally: 475
No try/catch/finally: 475

   try/catch/finally: 476
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 474

Sie sehen, dass das Ergebnis nicht relevant ist. In einigen Fällen ist die Version mit Try / Catch tatsächlich schneller!

Scheu
quelle
1
Ich habe das auch bemerkt, manchmal ist es mit try / catch schneller. Ich habe es zu Bens Antwort kommentiert. Im Gegensatz zu 24 Wählern mag ich diese Art von Benchmarking jedoch nicht. Ich denke nicht, dass dies ein guter Hinweis ist. Der Code ist in diesem Fall schneller, aber wird es immer sein?
Kobi
5
Beweist dies nicht, dass Ihre Maschine gleichzeitig eine Vielzahl anderer Aufgaben erledigte? Die verstrichene Zeit ist niemals ein gutes Maß. Sie müssen einen Profiler verwenden, der die Prozessorzeit und nicht die verstrichene Zeit aufzeichnet.
Colin Desmond
2
@Kobi: Ich bin damit einverstanden, dass dies nicht der beste Weg zum Benchmarking ist, wenn Sie es als Beweis dafür veröffentlichen, dass Ihr Programm schneller als andere oder so läuft, aber Sie als Entwickler einen Hinweis darauf geben können, dass eine Methode besser als eine andere ist . In diesem Fall können wir meiner Meinung nach sagen, dass die Unterschiede (zumindest für die Release-Konfiguration) ignorierbar sind.
Ehrfurcht
1
Du bist hier nicht am Timing 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.
Abel
15

Ich habe die tatsächlichen Auswirkungen von a try..catchin 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 ...

Guffa
quelle
11

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.

RHicke
quelle
8

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

Isaac
quelle
3
Komplexität ist O (1), es bedeutet nicht zu viel. Wenn Sie beispielsweise einen Codeabschnitt, der sehr häufig aufgerufen wird, mit try-catch ausstatten (oder eine Schleife erwähnen), können sich die O (1) am Ende zu einer messbaren Zahl addieren.
Csaba Toth
6

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:

Action q;
double thing1()
  { double total; for (int i=0; i<1000000; i++) total+=1.0/i; return total;}
double thing2()
  { q=null; return 1.0;}
...
x=thing1();     // statement1
x=thing2(x);    // statement2
doSomething(x); // statement3

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:

x=thing1();      // statement1
try
{ x=thing2(x); } // statement2
catch { q(); }
doSomething(x);  // statement3

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 thing2eine Ausnahme Interlocked.CompareExchangeauslösen könnte, könnte ein anderer Thread eine verwenden, um zu bemerken, dass qgelöscht und auf gesetzt wurde Thread.ResetAbort, und dann eine Thread.Abort()Voranweisung ausführen, in die Anweisung2 ihren Wert geschrieben hat x. Dann catchwürde das Thread.ResetAbort()[über einen Delegaten q] 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.

Superkatze
quelle
5

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:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i < int.MaxValue; i++)
{
    if (i != 0)
    {
        int k = 10 / i;
    }
}
stopwatch.Stop();
Console.WriteLine($"With Checking: {stopwatch.ElapsedMilliseconds}");
stopwatch.Reset();
stopwatch.Start();
for (int i = 1; i < int.MaxValue; i++)
{
    try
    {
        int k = 10 / i;
    }
    catch (Exception)
    {

    }
}
stopwatch.Stop();
Console.WriteLine($"With Exception: {stopwatch.ElapsedMilliseconds}");

Hier ist das Ergebnis:

With Checking: 20367
With Exception: 13998
Ted Oddman
quelle
4

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.

Ira Baxter
quelle
Was ich beschrieben habe, sind zwei verschiedene Ansätze zur Implementierung von Ausnahmen. Die Ansätze gelten gleichermaßen für C ++ und C # sowie für verwalteten / nicht verwalteten Code. Welche MS für ihr C # ausgewählt hat, weiß ich nicht genau, aber die von MS bereitgestellte Ausnahmebehandlungsarchitektur von Anwendungen auf Maschinenebene verwendet das schnellere Schema. Ich wäre ein bisschen überrascht, wenn die C # -Implementierung für 64 Bit sie nicht verwenden würde.
Ira Baxter