Die Kosten von try und catch
-
Ich habe mir mal gedanken darüber gemacht, warum man nicht immer bei z.B. dieser Aussage:
int* x = new int;versucht eine eventuelle Exception aufzufangen, und dann dem Benutzer eine schöne Mitteilung sendet und dann das Programm herunterfährt.
Woran liegt das? Abgesehen davon, dass der Code dann wahrscheinlich sehr unleserlich wird (habe den Artikel hier im Forum über Exceptions gelesen), ist der C++ Programmierer einfach zu faul, die Wahrscheinlichkeit, dass ein solcher Fehler auftritt zu gering, oder sind die Kosten für einen try Aufruf einfach zu hoch?Das ist das, was mich interessiert, was Kostet ein try...catch behandlung den Prozessor?
-
SALOMON schrieb:
Das ist das, was mich interessiert, was Kostet ein try...catch behandlung den Prozessor?
Wenn Du 'throw' sagst, muss der Computer wissen wohin er werfen soll.
Sobald Du also 'try' sagst, muss erstmal ein Zeiger aktualisiert werden, der sagt wohin geworfen werden soll.
Sobald Du den 'try' Bereich verlässt, muss der Zeiger wieder zurückgesetzt werden.Das ist nicht übertrieben teuer. Teuer wird es, wenn einem wirklich was um die Ohren fliegt. Dann muss nämlich herausgefunden werden, was überhaupt
geworfen wurde. Das Stichwort sind hier virtuelle Klassen.
Das Auslösen einer Exception ist immer das Ziehen der Handbremse. Das, was unvorsichtige Väter als Beifahrer ihrer Töchter machen. Hoffentlich eine Ausnahme. Wenn nichts mehr geht, ist das sicherlich besser als keine Reaktion, aber es sollte eine absolute Ausnahme bleiben.
Dann muss das Programm wieder in einen kontrollierten Zustand gebracht werden, was aufwendig werden kann, wenn eine Exception an verschiedenen Stellen des try-Blocks geworfen werden kann.Solange alles glatt geht und der Vater die Handbremse nicht zieht, sind try-catch preiswert und teils schneller sogar schneller als konservative Programmierung, weil im Algorithmus selbst weniger Abfragen sind. Exceptions werden durch die LowLevel-Funktionen geworfen, was bedeutet, dass wenn Exceptions kommen, diese LowLevel-Funktionen das entschieden haben.
Diese Entscheidung wird inzwischen sehr häufig getroffen, so dass mehr und mehr Exceptions auftreten. Wer bei einem falschen Arrayzugriff eine Exception wünscht, muss bei jedem Zugriff prüfen, ob dieser gültig ist oder eine Exception geworfen werden soll.In konservativer Programmierung könnte man sich einmal über die Größe des Arrays bewußt werden und dann nach belieben und ohne weitere Prüfung darauf zugreifen.
Hier können Exceptions teuer werden, sogar wenn man sie gar nicht benutzt/abgefragt werden.//EDIT: Nebenher verbrauchen die Tabelle, die aus der aussortiert wird, wohin geworfen wird, ebenfalls Speicher. Es sind hier also auch Mehrkosten in Kauf zu nehmen.
Ich glaube in 'Effizient C++ programmieren' von Scott Meyers findet sich dazu ebenfalls ein Artikel.
-
Exceptions fängt man dort, wo man sie *sinnvoll* behandeln kann. Und das ist sicher nicht überall dort, wo du int* x = new int; schreibst. Du kannst ja schließlich nicht mitten in einer Bibliotheksfunktion eine Messagebox anzeigen. Wer weiß, ob deine Funktion überhaupt innerhalb einer GUI-Anwendung aufgerufen wird? Das ist ja gerade der Sinn von Exceptions: Fehlerentdeckung von Fehlerbehandlung trennen zu können.
Man sollte Code deshalb so schreiben, dass er Exception-sicher ist, er also keine Probleme während des Unwindings macht (dazu gehört auch, dass man dynamische allozierte Ressourcen nicht einfach nackte Zeiger hängt - Stichwort RAII) An zentralen Stellen (Modulübergänge, main, ...) baut man dann Exception-Handler ein, die irgendwelche sinnvollen Aktionen durchführen. Das kann dann durchaus auch das Anzeigen einer Messagebox sein.
-
SALOMON schrieb:
...warum man nicht immer bei z.B. dieser Aussage:
int* x = new int;versucht eine eventuelle Exception aufzufangen...
Ganz einfach:
- Unnötig und
- unübersichtlich.Die Frage sollte man andersherum stellen: Wer soll wann etwas mit einer exception anfangen ? Der sollte genau dann sein try/catch implementieren.
Und ehrlich gesagt: Was soll der Nutzer Deines Programms mit einer "exception" anfangen, wenn Du ihm gar nichts über den fachlichen Kontext sagen kannst (und das kannst Du meistens nicht, wenn Du auf der tiefsten technischen Ebene bist) ?
Gruß,
Simon2.
-
Es ging mir jetzt auch nicht um das Beispiel. Ich würde an dieser Stelle selbst nie eine Exceptionbehandlung starten...
Es gibt mir einfach darum, was es mich kostet, wenn ich einen try Block habe, aber keine Exception geworfen wird. Das Beispiel war einfach das erste, was mir eingefallen ist
Also Xin hat genau das geschrieben, was ich wissen wollte! Danke!
-
Unter Windows verwendet die Mehrheit aller C++-Anwendungen das Structured Exception Handling das in Windows implementiert ist. Kannst ja mal danach suchen in der msdn, wenn dich interressiert wie das im Detail ablauft.
-
ja werde ich mir auch noch einmal anschauen

-
Sobald Du also 'try' sagst, muss erstmal ein Zeiger aktualisiert werden, der sagt wohin geworfen werden soll.
Sobald Du den 'try' Bereich verlässt, muss der Zeiger wieder zurückgesetzt werden.Nicht unbedingt. Ein Compiler kann es auch so machen dass er schöne Tables erstellt, wo er dann bei jedem "throw" mit Hilfe des instruction-pointer und des Stacks nachgucken kann, welche catch Blöcke alle "aktiv" sind. Die Liste klappert die Runtime dann der Reihe nach durch bis sie einen passenden findet. Welchen Code er beim Stack-Unwinding ausführen muss kann er auch über Tables + instruction pointer + den Stack ermitteln.
So ein Compiler muss dann nix an Code erzeugen was ausgeführt werden würde ohne dass wirklich eine Exception fliegt.
-
hustbaer schrieb:
Sobald Du also 'try' sagst, muss erstmal ein Zeiger aktualisiert werden, der sagt wohin geworfen werden soll.
Sobald Du den 'try' Bereich verlässt, muss der Zeiger wieder zurückgesetzt werden.Nicht unbedingt. Ein Compiler kann es auch so machen dass er schöne Tables erstellt, wo er dann bei jedem "throw" mit Hilfe des instruction-pointer und des Stacks nachgucken kann, welche catch Blöcke alle "aktiv" sind. Die Liste klappert die Runtime dann der Reihe nach durch bis sie einen passenden findet. Welchen Code er beim Stack-Unwinding ausführen muss kann er auch über Tables + instruction pointer + den Stack ermitteln.
So ein Compiler muss dann nix an Code erzeugen was ausgeführt werden würde ohne dass wirklich eine Exception fliegt.
Bei aktueller Programmierweise, wo immer mehr try-Blöcke propagiert werden, dürfte diese Tabelle recht groß ausfallen und die Suche nach dem richtigen Eintrag dadurch
recht langwierig.
Weiterhin springen viele Leute auf einen Exception-Hype auf, so dass außergewöhnliche Rückgaben wie z.B. Dateiende inzwischen als Exception geworfen werden. Prüft man nun auf die Exception Dateiende, wird diese Exception-Tabelle-Tabelle Liste bei jeder zu lesenden Datei durchwühlt, um dann in der richtigen Exception-Tabelle die richtige Reaktion auf die Exception zu finden.Die Idee der Exception finde ich hervorragend, es kann Code, der nur ausnahmsweise fehlschlagen kann, übersichtlicher machen.
Aber die Folgekosten sind mir persönlich zu hoch.[Das unsinnige Argument, der schnellen CPUs möge man mir bitte ersparen...]
-
@Xin:
Das Suchen in den Tabellen, die, wie du richtig sagst, recht gross werden können, wird nicht notwendigerweise langsam, wenn die Datenstruktur in der man diese Tabellen ablegt entsprechend ist (Baum, Skip-List, wo's Sinn macht evtl. nen Hash etc.). Und dass die Tabellen gross werden ist wirklich nur dann von Bedeutung wenn man wenig Speicher hat - die müssen ja nicht bei Programmstart gleich vollständig in den Speicher geladen werden - von daher dauert das auch nicht so schlimm lange.Vonwegen langsam - Exceptions sollten nicht verwendet werden um flow control zu machen, wenn etwas häufig vorkommt - wie du auch schon richtig festgestellt hast. Und dann ist es auch "ziemlich egal" wie lange es dauert, solange eben keine fliegen.
Was die "immer mehr try Blöcke" angeht - ich bin eigentlich gegen viel try Blöcke. "Guards" sind viel besser
Viele try Blöcke sind IMHO ein Zeichen von schlechtem Design, oder davon dass jmd. nicht verstanden hat wie man Exceptions sinnvoll einsetzen kann.Errorhandling ist aber so oder so (mit oder ohne Exceptions) ein schwieriges Thema. Auf jeden Fall bin ich der Meinung dass man innerhalb von einem Programm nicht mischen sollte. Jack. Pfui. Funktionen/Methoden die im Fehlerfall entweder mit einem Errorcode oder aber einer Exception zurückkommen sind fürchterlich.
-
hustbaer schrieb:
@Xin:
Das Suchen in den Tabellen, die, wie du richtig sagst, recht gross werden können, wird nicht notwendigerweise langsam, wenn die Datenstruktur in der man diese Tabellen ablegt entsprechend ist (Baum, Skip-List, wo's Sinn macht evtl. nen Hash etc.). Und dass die Tabellen gross werden ist wirklich nur dann von Bedeutung wenn man wenig Speicher hat - die müssen ja nicht bei Programmstart gleich vollständig in den Speicher geladen werden - von daher dauert das auch nicht so schlimm lange.Dann müssten sie nachgeladen werden, was kostenmäßig den Super-GAU darstellt.
hustbaer schrieb:
Vonwegen langsam - Exceptions sollten nicht verwendet werden um flow control zu machen, wenn etwas häufig vorkommt - wie du auch schon richtig festgestellt hast. Und dann ist es auch "ziemlich egal" wie lange es dauert, solange eben keine fliegen.
Falsch - oder zumindest zu kurz gedacht.
Wenn Exceptions fliegen könnten, dann muss für jede Exception geprüft werden, ob sie jetzt geworfen werden soll oder ob doch alles im so läuft, wie man sich das vorstellt.
Je tiefer die Prüfungen stattfinden, desto häufiger finden sie statt. Lasst man den Algorithmus laufen und guckt nur an den Stellen, an denen der Algorithmus falsche Werte liefern kann, ob es falsche Werte gibt, dann wäre das schneller.Ein Beispiel wäre eine Exception für eine Wurzelfunktion, die prüft, ob die Wurzel aus negativen Werten gezogen werden soll.
Bildet man das Skalarprodukt, wird sich niemals ein negativer Wert einfinden, die Prüfung, ob man jetzt eine Exception wegen einer negativen Eingabe machen muss, hält also nur auf.Auch das sind Kosten, die auf Exceptionhandling zurückgehen.
hustbaer schrieb:
Was die "immer mehr try Blöcke" angeht - ich bin eigentlich gegen viel try Blöcke. "Guards" sind viel besser
Viele try Blöcke sind IMHO ein Zeichen von schlechtem Design, oder davon dass jmd. nicht verstanden hat wie man Exceptions sinnvoll einsetzen kann.Hier sind wir absolut einer Meinung.
Allerdings sehe ich die Möglichkeit der Exceptions durchaus als Vorteil an. Es gibt auch gute Gründe für Exceptions, aber viel weniger, als Exceptions benutzt werden.
Exceptions sind eben keine zusätzliche Möglichkeit eines Rückgabewertes, wie es offenbar gelegentlich verstanden wird.
-
Xin schrieb:
Falsch - oder zumindest zu kurz gedacht.
Wenn Exceptions fliegen könnten, dann muss für jede Exception geprüft werden, ob sie jetzt geworfen werden soll oder ob doch alles im so läuft, wie man sich das vorstellt.
Je tiefer die Prüfungen stattfinden, desto häufiger finden sie statt. Lasst man den Algorithmus laufen und guckt nur an den Stellen, an denen der Algorithmus falsche Werte liefern kann, ob es falsche Werte gibt, dann wäre das schneller.Ein Beispiel wäre eine Exception für eine Wurzelfunktion, die prüft, ob die Wurzel aus negativen Werten gezogen werden soll.
Bildet man das Skalarprodukt, wird sich niemals ein negativer Wert einfinden, die Prüfung, ob man jetzt eine Exception wegen einer negativen Eingabe machen muss, hält also nur auf.Auch das sind Kosten, die auf Exceptionhandling zurückgehen.
Äh, nein. Ich denke es macht keinen Sinn "exceptions" mit "garkein error handling" zu vergleichen, dann wird was geschwindigkeit angeht immer "garkein error handling" gewinnen, das ist klar.
Aber vergleich mal error handling über exceptions mit error handling über return codes. Wobei die Prüfung "ist fehler?" natürlich an den gleichen Stellen zu passieren hat. Dann siehts die Sache nämlich schon wieder ganz anders aus. Dann muss nämlich nicht nur auf unterster Ebene 1x geprüft werden, sondern auf allen Schichten dazwischen nochmal. Jack. Pfui.
-
hustbaer schrieb:
Äh, nein. Ich denke es macht keinen Sinn "exceptions" mit "garkein error handling" zu vergleichen, dann wird was geschwindigkeit angeht immer "garkein error handling" gewinnen, das ist klar.
Aber vergleich mal error handling über exceptions mit error handling über return codes. Wobei die Prüfung "ist fehler?" natürlich an den gleichen Stellen zu passieren hat. Dann siehts die Sache nämlich schon wieder ganz anders aus. Dann muss nämlich nicht nur auf unterster Ebene 1x geprüft werden, sondern auf allen Schichten dazwischen nochmal. Jack. Pfui.
Ich denke, hier prallen zwei Philosophien aufeinander.
Wann immer möglich, möchte ich kontrollierte Daten in meinem Programm haben.
Das heißt, dass ich mir vorher überlege, was erlaubt ist - und was nicht, also dafür sorge, dass keine unkontrollierten Daten in meine Algorithmen kommen.Dann schreibe ich Algorithmen, die unter den notwendigen Bedingungen funktionieren - ohne irgendeine Kontrolle, ob die Daten mit denen sie gefüttert werden einen Sinn ergeben. Das habe ich ja bereits vorher getan. Das heißt, mir ist egal, was die Funktionen ausgeben, wenn sie mit unsinnigen Werten gefüttert werden. Wer dumme Fragen stellt, bekommt dumme Antworten.
Ich sorge auf möglichst hoher Ebene, dass keine Dummen fragen gestellt werden.
Dadrunter benötige ich keine Fehlerbehandlung mehr.Mich persönlich stört es überhaupt nicht, ob ein Programm eine Aufgabe nicht lösen kann, weil es abschmiert oder ob es eine Aufgabe nicht lösen kann, weil es eine Exception wirft und abbricht.
Bug ist Bug.
Wenn das Exceptionhandling dazu führt, das ich noch eine Meldung bekomme wie 'Unerwarteter Fehler', dann frage ich mich, wieso der Fehler doch immerhin so erwartet kam, dass man es geschafft hat eine Message-Box dazu zu programmieren. Irgendwie komme ich mir als User da ein wenig veräppelt vor.
Programme, die mit Hilfe von Exceptions wieder in einen funktionsfähigen Zustand gebracht werden, also die Aufgabe dann tatsächlich ohne Neustart lösen können sind wohl eher eine Exception.
Und genau da sehe ich die Aufgabe von Exceptions.
Die Ampelsteuerung schmiert ab, Exception, erstmal alles anhalten und sicherstellen, dass die Kreuzung frei ist, dann Techniker informieren, dann in aller Ruhe ganz von vorne anfangen, notfalls Warnlicht. Das ist in meinen Augen eine Anwendung, für die sich Exceptions hervorragend eignen.
-
Neh, da prallen keine zwei Philosophien aufeinander. Ich rede von Laufzeitfehlern. Exceptions sollten für Laufzeitfehler verwendet werden. Jeder Zugriff auf die Hardware z.B. kann daneben gehen, und ich möchte nicht dass mein Programm einfach so ohne Meldung abschmiert weil das Diskettenlaufwerk am sterben ist oder ein File nicht gefunden wurde etc.
Mich persönlich stört es überhaupt nicht, ob ein Programm eine Aufgabe nicht lösen kann, weil es abschmiert oder ob es eine Aufgabe nicht lösen kann, weil es eine Exception wirft und abbricht.
Bug ist Bug.
Mich würde es doch sehr stören wenn ein Programm einfach 42 als Lösung ausspuckt, ohne Fehlermeldung, als ob alles glatt gegangen wäre, wenn doch die richtige Lösung 100 ist. Als Beispiel. Ohne error checking kann dir genau das passieren. Weiss nicht ob das so toll ist.
-
hustbaer schrieb:
Neh, da prallen keine zwei Philosophien aufeinander. Ich rede von Laufzeitfehlern. Exceptions sollten für Laufzeitfehler verwendet werden. Jeder Zugriff auf die Hardware z.B. kann daneben gehen, und ich möchte nicht dass mein Programm einfach so ohne Meldung abschmiert weil das Diskettenlaufwerk am sterben ist oder ein File nicht gefunden wurde etc.
Lies mein vorheriges Posting nochmal. Daten in einem gültigen Format vorliegen haben, dann keine überflüssigen Abfragen mehr.
Daten, die ich nicht vorliegen habe, ... da brauche ich mir auch keine Gedanken machen, ob die gültig sein könnten.Weiterhin ist das Nicht-Auffinden von Daten keine derartige Besonderheit, dass es eine Exception erfordern würde.
Eine Exception könnte ich akzeptieren, wenn das Laufwerk während des Lesens verschwindet, weil jemand über das USB-Kabel gestolpert oder das Netzwerk zusammengebrochen ist.Wenn man Exceptions vermeidet, heißt das schließlich nicht, dass es kein Error-Handling gibt.
Genauso werden Usereingaben geprüft - bevor sie in einen Algorithmus wandern. Der Algorithmus wird mit falschen Werten gar nicht erst belastet.hustbaer schrieb:
Mich würde es doch sehr stören wenn ein Programm einfach 42 als Lösung ausspuckt, ohne Fehlermeldung, als ob alles glatt gegangen wäre, wenn doch die richtige Lösung 100 ist. Als Beispiel. Ohne error checking kann dir genau das passieren. Weiss nicht ob das so toll ist.
Das kann Dir mit Prüfungen genauso passieren. Fehlerhafte Algorithmen werden nicht besser, wenn sie durch dauernde Prüfungen verlangsamt werden.
Daten, die ungültig sind und das Ergebnis verfälschen, werden vorher abgefangen. Da ist das Error-Checking.
Kompliziertere Alrithmen müssen in ihren möglichen Wegen getestet werden. Welche Eingaben sind möglich, wieviele Fälle treten auf, ist für jeden Fall ein Testfall mit bekannter Lösung vorhanden?Dann wird mit den Daten gerechnet - ohne Error-Checking. Kommt dann 42 statt 100 raus, hat das Programm einen Bug. Da helfen auch Prüfungen nichts mehr. Das kann Dir genauso gut mit 20000 zusätzlichen Abfragen passieren. Ein paar Asserts beim Testen gerne. Aber wenn das Programm ausgeliefert wird, ist es entweder korrekt - oder buggy.
Mit oder ohne Abfragen.Ich habe auch nichts gegen Abfragen, im Algorithmus - wenn sie notwendig sind.
Aber die berühmte Wurzel, die prüft ob der Parameter negativ ist, interessiert mich nicht.
Wenn ich z.B. eine Vektorlänge berechne ist es absolut unmöglich, dass ich aus einer negativen Zahl die Wurzel ziehe. Berechne ich ein paar Millionen Vektoren, sind das ein paar Millionen überflüssiger Abfragen. Ich will nicht, dass sqrt() meine Eingabe prüft in Fällen, wo dies nicht nötig ist.Wenn mein Algorithmus negative Zahlen ausspucken kann und diese Zahlen sqrt() übergibt, dann ist mein Algorithmus dafür verantwortlich, wenn da Müll rauskommt - nicht sqrt().
Dann muss mein Algorithmus an dieser das prüfen und gegebenfalls die Information zurückliefern, wenn was schlief läuft. Dafür habe ich mir vorher Gedanken gemacht, welche Wege meine Funktion gehen kann und in welchem Wertebereichen sich die Variablen befinden können.
Aber eine Abfrage an der richtigen Stelle reicht, das muss nicht in jeder Funktion geprüft werden.Das kann man als Entwickler gerne mit einer speziellen Debug-Version der entsprechenden Libs prüfen, aber der fertige Algorithmus darf keine Sicherheitsleinen mehr brauchen, sondern garantiert in exakt definierten Bahnen verlaufen.