571 Stimmen

Welche Auswirkungen haben Ausnahmen auf die Leistung in Java?

Frage: Ist die Ausnahmebehandlung in Java wirklich langsam?

Konventionelle Weisheit und auch viele Google-Ergebnisse besagen, dass außergewöhnliche Logik nicht für den normalen Programmablauf in Java verwendet werden sollte. Dafür werden in der Regel zwei Gründe genannt,

  1. er ist wirklich langsam - sogar um eine Größenordnung langsamer als normaler Code (die angegebenen Gründe sind unterschiedlich),

y

  1. es ist chaotisch, weil die Leute erwarten, dass nur Fehler in außergewöhnlichem Code behandelt werden.

Diese Frage bezieht sich auf die Nummer 1.

Ein Beispiel, diese Seite beschreibt die Java-Ausnahmebehandlung als "sehr langsam" und bezieht die Langsamkeit auf die Erstellung der Ausnahmemeldungszeichenfolge - "diese Zeichenfolge wird dann bei der Erstellung des Ausnahmeobjekts verwendet, das ausgelöst wird. Das ist nicht schnell." Der Artikel Effektive Ausnahmebehandlung in Java sagt, dass "der Grund dafür in dem Aspekt der Objekterzeugung bei der Ausnahmebehandlung liegt, der das Auslösen von Ausnahmen von Natur aus langsam macht". Ein anderer Grund ist, dass die Stack-Trace-Generierung die Ursache für die Verlangsamung ist.

Meine Tests (mit Java 1.6.0_07, Java HotSpot 10.0, auf 32-Bit-Linux) zeigen, dass die Ausnahmebehandlung nicht langsamer ist als normaler Code. Ich habe versucht, eine Methode in einer Schleife auszuführen, die einen Code ausführt. Am Ende der Methode verwende ich einen Booleschen Wert, um anzugeben, ob return o werfen . Auf diese Weise ist die tatsächliche Verarbeitung die gleiche. Ich habe versucht, die Methoden in verschiedenen Reihenfolgen auszuführen und den Durchschnitt meiner Testzeiten zu ermitteln, weil ich dachte, dass die JVM sich aufwärmen könnte. In allen meinen Tests war der Wurf mindestens so schnell wie die Rückkehr, wenn nicht sogar schneller (bis zu 3,1 % schneller). Ich bin völlig offen für die Möglichkeit, dass meine Tests falsch waren, aber ich habe in den letzten ein oder zwei Jahren nichts in Form von Codebeispielen, Testvergleichen oder Ergebnissen gesehen, die zeigen, dass die Ausnahmebehandlung in Java tatsächlich langsam ist.

Was mich auf diesen Weg geführt hat, war eine API, die ich verwenden musste und die Ausnahmen als Teil der normalen Steuerungslogik auslöste. Ich wollte sie in ihrer Verwendung korrigieren, aber jetzt kann ich das vielleicht nicht mehr. Werde ich sie stattdessen für ihr vorausschauendes Denken loben müssen?

In dem Papier Effiziente Java-Ausnahmebehandlung bei der Just-in-Time-Kompilierung Die Autoren weisen darauf hin, dass das Vorhandensein von Ausnahmebehandlungsroutinen allein, auch wenn keine Ausnahmen ausgelöst werden, ausreicht, um den JIT-Compiler daran zu hindern, den Code ordnungsgemäß zu optimieren und ihn somit zu verlangsamen. Ich habe diese Theorie noch nicht getestet.

13 Stimmen

Ich weiß, dass Sie nicht nach 2) gefragt haben, aber Sie sollten wirklich erkennen, dass die Verwendung einer Ausnahme für den Programmablauf nicht besser ist als die Verwendung von GOTOs. Einige Leute verteidigen Gotos, andere würden das verteidigen, wovon Sie sprechen, aber wenn Sie jemanden fragen, der beides über einen längeren Zeitraum implementiert und gewartet hat, wird er Ihnen sagen, dass beides schlechte, schwer zu wartende Entwurfspraktiken sind (und er wird wahrscheinlich den Namen der Person verfluchen, die dachte, sie sei klug genug, um die Entscheidung zu treffen, sie zu verwenden).

98 Stimmen

Bill, die Behauptung, die Verwendung von Ausnahmen für den Programmablauf sei nicht besser als die Verwendung von GOTOs, ist nicht besser als die Behauptung, die Verwendung von Konditionalen und Schleifen für den Programmablauf sei nicht besser als die Verwendung von GOTOs. Das ist ein Ablenkungsmanöver. Erkläre dich selbst. Ausnahmen können und werden in anderen Sprachen effektiv für den Programmfluss verwendet. Idiomatischer Python-Code verwendet zum Beispiel regelmäßig Ausnahmen. Ich kann und habe Code gepflegt, der Ausnahmen auf diese Weise verwendet (allerdings nicht in Java), und ich glaube nicht, dass daran etwas grundsätzlich falsch ist.

5 Stimmen

Beachten Sie, dass einige Web-Frameworks Exceptions als bequeme Möglichkeit zur Umleitung verwenden - z.B. Wicket's RestartResponseException . Es passiert nur wenige Male pro Anfrage, normalerweise nicht, und ich kann mir kaum einen bequemeren Weg in einem Java-orientierten Komponenten-Framework vorstellen.

10voto

Alan Moore Punkte 70949

Vor einiger Zeit habe ich eine Klasse geschrieben, um die relative Leistung der Konvertierung von Strings in Ints mit zwei Ansätzen zu testen: (1) Aufruf von Integer.parseInt() und Abfangen der Ausnahme oder (2) Abgleich des Strings mit einem Regex und Aufruf von parseInt() nur bei erfolgreicher Übereinstimmung. Ich habe den Regex so effizient wie möglich verwendet (d.h. ich habe die Objekte Pattern und Matcher vor der Schleife erstellt), und ich habe die Stacktraces der Ausnahmen nicht gedruckt oder gespeichert.

Bei einer Liste von zehntausend Zeichenketten war der parseInt()-Ansatz viermal so schnell wie der Regex-Ansatz, wenn sie alle gültige Zahlen waren. Wenn jedoch nur 80 % der Zeichenfolgen gültig waren, war der Regex doppelt so schnell wie parseInt(). Und wenn 20 % gültig waren, d. h. die Ausnahme wurde in 80 % der Fälle ausgelöst und abgefangen, war der Regex etwa zwanzigmal so schnell wie parseInt().

Ich war von dem Ergebnis überrascht, da der Regex-Ansatz gültige Zeichenketten zweimal verarbeitet: einmal für die Übereinstimmung und ein zweites Mal für parseInt(). Aber das Werfen und Abfangen von Ausnahmen hat das mehr als wettgemacht. Diese Art von Situation wird in der Praxis wahrscheinlich nicht sehr oft vorkommen, aber wenn doch, sollten Sie auf keinen Fall die Technik des Abfangens von Ausnahmen verwenden. Wenn Sie aber nur Benutzereingaben oder Ähnliches überprüfen, sollten Sie auf jeden Fall den parseInt()-Ansatz verwenden.

8voto

Lars Westergren Punkte 2045

Ich glaube, der erste Artikel bezieht sich auf das Durchlaufen des Aufrufstapels und das Erstellen einer Stapelverfolgung als den teuren Teil, und obwohl der zweite Artikel dies nicht sagt, denke ich, dass dies der teuerste Teil der Objekterstellung ist. John Rose hat einen Artikel, in dem er verschiedene Techniken zur Beschleunigung von Ausnahmen beschreibt . (Vorabzuweisung und Wiederverwendung einer Ausnahme, Ausnahmen ohne Stapelspuren usw.)

Dennoch sollte dies nur als notwendiges Übel, als letzter Ausweg betrachtet werden. Johns Grund dafür ist, Funktionen in anderen Sprachen zu emulieren, die in der JVM (noch) nicht verfügbar sind. Sie sollten sich NICHT angewöhnen, Ausnahmen für den Kontrollfluss zu verwenden. Schon gar nicht aus Leistungsgründen! Wie Sie selbst in #2 erwähnen, laufen Sie Gefahr, auf diese Weise schwerwiegende Fehler in Ihrem Code zu verbergen, und er wird für neue Programmierer schwieriger zu warten sein.

Mikrobenchmarks in Java sind erstaunlich schwer zu bewerkstelligen (so wurde mir gesagt), vor allem, wenn man sich in JIT-Gebiet begibt, so dass ich wirklich bezweifle, dass die Verwendung von Ausnahmen schneller ist als "Return" im wirklichen Leben. Ich vermute zum Beispiel, dass Sie zwischen 2 und 5 Stackframes in Ihrem Test haben? Stellen Sie sich nun vor, dass Ihr Code von einer JSF-Komponente aufgerufen wird, die von JBoss bereitgestellt wird. Jetzt haben Sie vielleicht einen Stack-Trace, der mehrere Seiten lang ist.

Vielleicht können Sie Ihren Testcode veröffentlichen?

8voto

BorisOkunskiy Punkte 1780

Ich weiß nicht, ob diese Themen zusammenhängen, aber ich wollte einmal einen Trick implementieren, der sich auf den Stack-Trace des aktuellen Threads stützt: Ich wollte den Namen der Methode herausfinden, die die Instanziierung innerhalb der instanziierten Klasse ausgelöst hat (jaja, die Idee ist verrückt, ich habe sie aufgegeben). So habe ich herausgefunden, dass der Aufruf von Thread.currentThread().getStackTrace() est extrem langsam (aufgrund der nativen dumpThreads Methode, die er intern verwendet).

Also Java Throwable hat dementsprechend eine eigene Methode fillInStackTrace . Ich glaube, dass der Mörder catch Block, der zuvor beschrieben wurde, löst irgendwie die Ausführung dieser Methode aus.

Aber lassen Sie mich Ihnen eine andere Geschichte erzählen...

In Scala werden einige funktionale Merkmale in der JVM kompiliert, indem ControlThrowable die die Throwable und übersteuert dessen fillInStackTrace auf folgende Weise:

override def fillInStackTrace(): Throwable = this

Also habe ich den obigen Test angepasst (die Anzahl der Zyklen wurde um zehn verringert, mein Rechner ist etwas langsamer :):

class ControlException extends ControlThrowable

class T {
  var value = 0

  def reset = {
    value = 0
  }

  def method1(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      println("You'll never see this!")
    }
  }

  def method2(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      throw new Exception()
    }
  }

  def method3(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new Exception()
    }
  }

  def method4(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new ControlException()
    }
  }
}

class Main {
  var l = System.currentTimeMillis
  val t = new T
  for (i <- 1 to 10000000)
    t.method1(i)
  l = System.currentTimeMillis - l
  println("method1 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method2(i)
  } catch {
    case _ => println("You'll never see this")
  }
  l = System.currentTimeMillis - l
  println("method2 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method4(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method4 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method3(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method3 took " + l + " ms, result was " + t.value)

}

Die Ergebnisse sind also:

method1 took 146 ms, result was 2
method2 took 159 ms, result was 2
method4 took 1551 ms, result was 2
method3 took 42492 ms, result was 2

Sie sehen, der einzige Unterschied zwischen method3 y method4 ist, dass sie unterschiedliche Arten von Ausnahmen auslösen. Ja, ja, method4 ist immer noch langsamer als method1 y method2 aber der Unterschied ist weitaus akzeptabler.

5voto

James Schek Punkte 17598

Ich habe einige Leistungstests mit JVM 1.5 durchgeführt und die Verwendung von Ausnahmen war mindestens 2x langsamer. Im Durchschnitt: Die Ausführungszeit einer trivialen kleinen Methode hat sich mit Ausnahmen mehr als verdreifacht (3x). Bei einer trivialen kleinen Schleife, die die Ausnahme abfangen musste, stieg die Eigenzeit um das Doppelte.

Ich habe ähnliche Zahlen im Produktionscode und in Mikro-Benchmarks gesehen.

Ausnahmen sollten auf jeden Fall NICHT für alles verwendet werden, was häufig aufgerufen wird. Tausende von Ausnahmen pro Sekunde zu machen, würde einen riesigen Engpass verursachen.

Zum Beispiel die Verwendung von "Integer.ParseInt(...)", um alle schlechten Werte in einer sehr großen Textdatei zu finden - eine sehr schlechte Idee. (Ich habe diese Utility-Methode gesehen töten Leistung bei Produktionscode)

Die Verwendung einer Ausnahme, um einen fehlerhaften Wert in einem GUI-Formular zu melden, ist aus Sicht der Leistung wahrscheinlich nicht so schlecht.

Ob es eine gute Design-Praxis ist oder nicht, würde ich mit der Regel gehen: wenn der Fehler normal/erwartet ist, dann verwenden Sie einen Rückgabewert. Wenn er abnormal ist, verwende eine Ausnahme. Zum Beispiel: Beim Lesen von Benutzereingaben sind schlechte Werte normal - verwenden Sie einen Fehlercode. Bei der Übergabe eines Wertes an eine interne Dienstfunktion sollten schlechte Werte durch den aufrufenden Code gefiltert werden - verwenden Sie eine Ausnahme.

5voto

David Jeske Punkte 2106

Die Leistung von Ausnahmen in Java und C# lässt viel zu wünschen übrig.

Als Programmierer zwingt uns dies dazu, nach der Regel "Ausnahmen sollten selten verursacht werden" zu leben, einfach aus praktischen Leistungsgründen.

Als Informatiker sollten wir uns jedoch gegen diesen problematischen Zustand auflehnen. Die Person, die eine Funktion schreibt, hat oft keine Ahnung, wie oft sie aufgerufen wird, oder ob Erfolg oder Misserfolg wahrscheinlicher ist. Nur der Aufrufer hat diese Informationen. Der Versuch, Ausnahmen zu vermeiden, führt zu unklaren API-Idomen, bei denen wir in einigen Fällen nur saubere, aber langsame Ausnahmeversionen und in anderen Fällen schnelle, aber schwerfällige Rückgabefehler haben, und in wieder anderen Fällen haben wir am Ende beides. Der Bibliotheksimplementierer muss möglicherweise zwei Versionen von APIs schreiben und pflegen, und der Aufrufer muss entscheiden, welche der beiden Versionen er in jeder Situation verwenden will.

Das ist ein ziemliches Durcheinander. Wenn Ausnahmen eine bessere Leistung hätten, könnten wir diese klobigen Idiome vermeiden und Ausnahmen so verwenden, wie sie gedacht sind... als strukturierte Fehlerrückgabe.

Ich würde wirklich gerne sehen, dass Ausnahmemechanismen mit Techniken implementiert werden, die näher an den Rückgabewerten liegen, so dass wir die Leistung näher an den Rückgabewerten haben könnten da dies das ist, worauf wir bei leistungsempfindlichem Code zurückgreifen.

Hier ist ein Code-Beispiel, das die Leistung von Ausnahmen mit der Leistung von Fehler-Rückgabewerten vergleicht.

public class TestIt {

int value;

public int getValue() {
    return value;
}

public void reset() {
    value = 0;
}

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

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

public void exception_error(boolean shouldfail, int recurse_depth) throws Exception {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            throw new Exception();
        }
    } 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 = 100000000;

    // (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;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                t.baseline_null(shoulderror,recurse_depth);
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("baseline: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    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;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                if (!t.retval_error(shoulderror,recurse_depth)) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("retval_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    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;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                try {
                    t.exception_error(shoulderror,recurse_depth);
                } catch (Exception e) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("exception_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);              
        }
    }
}

}

Und hier sind die Ergebnisse:

baseline: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 683 ms
baseline: recurse_depth 2, exception_freqeuncy 0.25 (0), time elapsed 790 ms
baseline: recurse_depth 2, exception_freqeuncy 0.5 (0), time elapsed 768 ms
baseline: recurse_depth 2, exception_freqeuncy 0.75 (0), time elapsed 749 ms
baseline: recurse_depth 2, exception_freqeuncy 1.0 (0), time elapsed 731 ms
baseline: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 923 ms
baseline: recurse_depth 5, exception_freqeuncy 0.25 (0), time elapsed 971 ms
baseline: recurse_depth 5, exception_freqeuncy 0.5 (0), time elapsed 982 ms
baseline: recurse_depth 5, exception_freqeuncy 0.75 (0), time elapsed 947 ms
baseline: recurse_depth 5, exception_freqeuncy 1.0 (0), time elapsed 937 ms
baseline: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1154 ms
baseline: recurse_depth 8, exception_freqeuncy 0.25 (0), time elapsed 1149 ms
baseline: recurse_depth 8, exception_freqeuncy 0.5 (0), time elapsed 1133 ms
baseline: recurse_depth 8, exception_freqeuncy 0.75 (0), time elapsed 1117 ms
baseline: recurse_depth 8, exception_freqeuncy 1.0 (0), time elapsed 1116 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 742 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 743 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 734 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 723 ms
retval_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 728 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 920 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 1121   ms
retval_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 1037 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 1141   ms
retval_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 1130 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1218 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 1334  ms
retval_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 1478 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 1637 ms
retval_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 1655 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 726 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 17487   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 33763   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 67367   ms
exception_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 66990 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 924 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 23775  ms
exception_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 46326 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 91707 ms
exception_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 91580 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1144 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 30440 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 59116   ms
exception_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 116678 ms
exception_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 116477 ms

Die Überprüfung und Weitergabe von Rückgabewerten verursacht zusätzliche Kosten im Vergleich zum Baseline-Null-Aufruf, und diese Kosten sind proportional zur Aufruf-Tiefe. Bei einer Aufrufkettentiefe von 8 war die Version mit Fehler-Rückgabewert-Prüfung etwa 27 % langsamer als die Basisversion, bei der die Rückgabewerte nicht geprüft wurden.

Die Leistung bei Ausnahmen hängt dagegen nicht von der Aufruftiefe ab, sondern von der Häufigkeit der Ausnahmen. Die Verschlechterung mit zunehmender Ausnahmehäufigkeit ist jedoch viel dramatischer. Bei einer Fehlerhäufigkeit von nur 25 % lief der Code 24 TIMES langsamer. Bei einer Fehlerhäufigkeit von 100 % ist die Ausnahmeversion fast 100 TIMES langsamer.

Das deutet für mich darauf hin, dass wir vielleicht die falschen Kompromisse bei unseren Ausnahmeimplementierungen eingehen. Ausnahmen könnten schneller sein, entweder durch die Vermeidung von kostspieligen Stalking-Walks oder durch die Umwandlung in eine compilerunterstützte Rückgabewertüberprüfung. Solange das nicht der Fall ist, müssen wir sie meiden, wenn wir wollen, dass unser Code schnell läuft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X