Wie teuer sind Ausnahmen in C #? Es scheint, dass sie nicht unglaublich teuer sind, solange der Stapel nicht tief ist; Ich habe jedoch widersprüchliche Berichte gelesen.
Gibt es einen endgültigen Bericht, der nicht widerlegt wurde?
Wie teuer sind Ausnahmen in C #? Es scheint, dass sie nicht unglaublich teuer sind, solange der Stapel nicht tief ist; Ich habe jedoch widersprüchliche Berichte gelesen.
Gibt es einen endgültigen Bericht, der nicht widerlegt wurde?
Antworten:
Jon Skeet hat im Januar 2006 Exceptions and Performance in .NET geschrieben
Welches wurde Ausnahmen und Performance Redux aktualisiert (danke @Gulzar)
Worauf Rico Mariani in Die wahren Kosten von .NET-Ausnahmen - Lösung einging
Siehe auch: Krzysztof Cwalina - Update der Designrichtlinien: Exception Throwing
Nachdem ich gelesen hatte, dass Ausnahmen in Bezug auf die Leistung kostspielig sind, habe ich ein einfaches Messprogramm zusammengestellt, das dem vor Jahren veröffentlichten von Jon Skeet sehr ähnlich ist . Ich erwähne dies hier hauptsächlich, um aktualisierte Zahlen bereitzustellen.
Das Programm benötigte weniger als 29914 Millisekunden, um eine Million Ausnahmen zu verarbeiten, was 33 Ausnahmen pro Millisekunde entspricht . Dies ist schnell genug, um Ausnahmen in den meisten Situationen zu einer praktikablen Alternative zu Rückkehrcodes zu machen.
Beachten Sie jedoch, dass mit Rückkehrcodes anstelle von Ausnahmen dasselbe Programm weniger als eine Millisekunde ausführt, was bedeutet, dass Ausnahmen mindestens 30.000 Mal langsamer sind als Rückkehrcodes . Wie von Rico Mariani betont, sind diese Zahlen auch Mindestzahlen. In der Praxis dauert das Werfen und Abfangen einer Ausnahme länger.
Gemessen auf einem Laptop mit Intel Core2 Duo T8100 bei 2,1 GHz mit .NET 4.0 im Release-Build, das nicht unter dem Debugger ausgeführt wird (was es viel langsamer machen würde).
Dies ist mein Testcode:
static void Main(string[] args)
{
int iterations = 1000000;
Console.WriteLine("Starting " + iterations.ToString() + " iterations...\n");
var stopwatch = new Stopwatch();
// Test exceptions
stopwatch.Reset();
stopwatch.Start();
for (int i = 1; i <= iterations; i++)
{
try
{
TestExceptions();
}
catch (Exception)
{
// Do nothing
}
}
stopwatch.Stop();
Console.WriteLine("Exceptions: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
// Test return codes
stopwatch.Reset();
stopwatch.Start();
int retcode;
for (int i = 1; i <= iterations; i++)
{
retcode = TestReturnCodes();
if (retcode == 1)
{
// Do nothing
}
}
stopwatch.Stop();
Console.WriteLine("Return codes: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
Console.WriteLine("\nFinished.");
Console.ReadKey();
}
static void TestExceptions()
{
throw new Exception("Failed");
}
static int TestReturnCodes()
{
return 1;
}
assert
wenn etwas Außergewöhnliches passiert. Die Fehlerbehandlung erfolgt mit Fehlercodes.
That is fast enough to make exceptions a viable alternative to return codes for most situations.
Ich würde nicht sagen, dass ein Aufrufstapel mit einer Tiefe von 2-3 wirklich "die meisten Situationen " darstellt. Tiefere Call-Stacks sollten wahrscheinlich ebenfalls berücksichtigt werden.
Ich glaube , ich im Lager bin , dass , wenn die Leistung von Ausnahmen Auswirkungen auf Ihre Anwendung dann sind Sie werfen WAY zu viele von ihnen. Ausnahmen sollten für außergewöhnliche Bedingungen gelten, nicht als routinemäßige Fehlerbehandlung.
Trotzdem erinnere ich mich daran, wie Ausnahmen behandelt werden, indem ich im Wesentlichen den Stapel entlang gehe und eine catch-Anweisung finde, die dem Typ der ausgelösten Ausnahme entspricht. Die Leistung wird also am meisten davon beeinflusst, wie tief Sie vom Fang entfernt sind und wie viele Fangaussagen Sie haben.
Barebones-Ausnahmeobjekte in C # sind ziemlich leicht; Es ist normalerweise die Fähigkeit, eine zu kapseln InnerException
, die es schwer macht, wenn der Objektbaum zu tief wird.
Was einen endgültigen Bericht betrifft, sind mir keine bekannt, obwohl ein flüchtiges dotTrace-Profil (oder ein anderer Profiler) für Speicherverbrauch und Geschwindigkeit ziemlich einfach zu erstellen sein wird.
In meinem Fall waren Ausnahmen sehr teuer. Ich habe das umgeschrieben:
public BlockTemplate this[int x,int y, int z]
{
get
{
try
{
return Data.BlockTemplate[World[Center.X + x, Center.Y + y, Center.Z + z]];
}
catch(IndexOutOfRangeException e)
{
return Data.BlockTemplate[BlockType.Air];
}
}
}
Das mögen:
public BlockTemplate this[int x,int y, int z]
{
get
{
int ix = Center.X + x;
int iy = Center.Y + y;
int iz = Center.Z + z;
if (ix < 0 || ix >= World.GetLength(0)
|| iy < 0 || iy >= World.GetLength(1)
|| iz < 0 || iz >= World.GetLength(2))
return Data.BlockTemplate[BlockType.Air];
return Data.BlockTemplate[World[ix, iy, iz]];
}
}
Und bemerkte eine gute Geschwindigkeitssteigerung von ca. 30 Sekunden. Diese Funktion wird beim Start mindestens 32K-mal aufgerufen. Code ist nicht so klar, was die Absicht ist, aber die Kosteneinsparungen waren enorm.
Ich habe meine eigenen Messungen durchgeführt, um herauszufinden, wie ernst die Auswirkungen auf Ausnahmen sind. Ich habe nicht versucht, die absolute Zeit für das Werfen / Fangen von Ausnahmen zu messen. Was mich am meisten interessiert hat, ist, wie viel langsamer eine Schleife wird, wenn in jedem Durchgang eine Ausnahme ausgelöst wird. Der Messcode sieht so aus
for(; ; ) {
iValue = Level1(iValue);
lCounter += 1;
if(DateTime.Now >= sFinish) break;
}
vs.
for(; ; ) {
try {
iValue = Level3Throw(iValue);
}
catch(InvalidOperationException) {
iValue += 3;
}
lCounter += 1;
if(DateTime.Now >= sFinish) break;
}
Der Unterschied beträgt 20 mal. Das zweite Snippet ist 20-mal langsamer.
Der Leistungsausfall mit Ausnahmen scheint an dem Punkt zu sein, an dem das Ausnahmeobjekt generiert wird (wenn auch zu klein, um in 90% der Fälle Bedenken zu verursachen). Die Empfehlung ist daher Ihren Code profilieren - wenn Ausnahmen sind eine Leistungseinbußen verursachen, schreiben Sie eine neue High-perf - Methode , die keine Ausnahmen nicht verwendet. (Ein Beispiel, das mir in den Sinn kommt, wäre (TryParse wurde eingeführt, um Perf-Probleme mit Parse zu überwinden, das Ausnahmen verwendet).
Allerdings verursachen Ausnahmen in den meisten Fällen in den meisten Situationen keine signifikanten Leistungseinbußen. Daher soll die MS Design Guideline Fehler melden, indem Ausnahmen ausgelöst werden
Nur um meine persönliche Erfahrung zu vermitteln: Ich arbeite mit NewtonSoft an einem Programm, das JSON-Dateien analysiert und Daten daraus extrahiert.
Ich habe das umgeschrieben:
try
{
name = rawPropWithChildren.Value["title"].ToString();
}
catch(System.NullReferenceException)
{
name = rawPropWithChildren.Name;
}
Dazu:
if(rawPropWithChildren.Value["title"] == null)
{
name = rawPropWithChildren.Name;
}
else
{
name = rawPropWithChildren.Value["title"].ToString();
}
Natürlich haben Sie nicht wirklich einen Kontext, um darüber zu urteilen, aber hier sind meine Ergebnisse:
( Im Debug-Modus )
Option 1 mit Ausnahmen. 38,50s
Option 2 mit Ausnahmen. 06.48s
Um ein wenig Kontext zu geben, arbeite ich mit Tausenden von JSON-Eigenschaften, die null sein können. Ausnahmen wurden viel zu oft ausgelöst, beispielsweise während 15% der Ausführungszeit. Nun, nicht wirklich präzise, aber sie wurden zu oft geworfen. Ich wollte das beheben, also änderte ich meinen Code und verstand nicht, warum die Ausführungszeit so viel schneller war. Das lag an meiner schlechten Ausnahmebehandlung.
Was ich daraus gelernt habe: Ich muss Ausnahmen nur in bestimmten Fällen und für Dinge verwenden, die nicht mit einer einfachen bedingten Anweisung getestet werden können. Sie müssen auch umso seltener geworfen werden.
Dies ist eine zufällige Geschichte für Sie, aber ich denke, ich würde definitiv zweimal überlegen, bevor ich ab sofort Ausnahmen in meinem Code verwende!
Ich habe kürzlich C # -Ausnahmen (werfen und fangen) in einer Summationsschleife gemessen, die bei jeder Addition einen arithmetischen Überlauf auslöste. Der Wurf und Fang des arithmetischen Überlaufs betrug auf einem Quad-Core-Laptop etwa 8,5 Mikrosekunden = 117 KiloExceptions / Sekunde.
Ausnahmen sind teuer, aber es steckt noch mehr dahinter, wenn Sie zwischen Ausnahme- und Rückkehrcodes wählen möchten.
Historisch gesehen war das Argument, Ausnahme stellt sicher, dass Code gezwungen ist, mit der Situation umzugehen, während Rückkehrcodes ignoriert werden können. Ich habe diese Argumente nie favorisiert, da kein Programmierer ihre Codes absichtlich ignorieren und brechen möchte - insbesondere ein gutes Testteam / oder ein gutes Testteam Ein gut geschriebener Testfall wird die Rückkehrcodes definitiv nicht ignorieren.
Aus Sicht der modernen Programmierpraktiken muss die Verwaltung von Ausnahmen nicht nur auf ihre Kosten, sondern auch auf ihre Rentabilität hin überprüft werden.
1. Da die meisten Frontends von der API getrennt werden, wird eine Ausnahme ausgelöst. zB eine mobile App mit Rest-API. Dieselbe API kann auch für ein eckiges js-basiertes Web-Frontend verwendet werden.
In beiden Szenarien werden Rückkehrcodes anstelle von Ausnahmen bevorzugt.
2 .. Heute versuchen Hacker nach dem Zufallsprinzip, alle Webdienstprogramme zu beschädigen. Wenn sie in einem solchen Szenario ständig Ihre Anmelde-API für Apps angreifen und die App ständig Ausnahmen auslöst, werden Sie täglich mit Tausenden von Ausnahmen konfrontiert. Natürlich werden viele sagen, dass die Firewall sich um solche Angriffe kümmert. Allerdings geben nicht alle Geld für die Verwaltung einer dedizierten Firewall oder eines teuren Anti-Spam-Dienstes aus. Es ist besser, wenn Ihr Code auf diese Szenarien vorbereitet ist.