Exzessives Exceptions werfen
-
ich würde auch sagen dass es eher schlechter Stil ist, ist aber wiedermal auch ein bisschen Ansichtssache.
Auf jeden Fall macht es den Code unnötig länger. Wegen der Performance: in normalen kurzen programmen sollte man davon sowieso fast nichts merken weil der pc das so schnell ausführt, ob da ein try{} mehr oder weniger ist merkt man gar nicht. Außerdem ist das ja wirklich fast wie return-value-checking, ich kann mir also nicht vorstellen dass es soo viel länger dauert. kannst es ja mal ausprobieren, einfach mal ne schleife schreiben die gaanz oft durchläuft und zB irgendwas ausgiebt, einmal mit den ganzen trys und einmal ohne und auf die zeit achten wielange das programm braucht, aber wahrscheinlich wirste da eh keinen unterschied merken.
mfg,
andi01.
-
Icematix schrieb:
Früher habe ich mich gesträubt Exceptions zu werfen, mittlerweile werfe ich äußerst gerne Exceptions und habe so gut wie sämtliches Return-Value-Checking entfernt.
Das ist total übertrieben.
find() soll ohne Exception anzeigen können, daß nichts gefunden wurde.
Ein Container soll sagen, ob er leer ist und nicht den User gegen eine Exception laufen lassen.
Denn das sind ganz normale Fälle und keine Ausnahmen.
Aufräumen soll man mit Destruktoren, nicht im nur dazu angelegten catch-Block.Nur ist mir aufgefallen dass so ziemlich jeder Codeteil mit etwas Relevanz in einem try {}-Block steckt.
Das muß ja unglaublich häßlich aussehen.
Ist das schlechter Stil ?
Ja.
Und was mich mehr interessiert, wirkt es sich auf die Performance aus?
Mal messen...
#include <iostream> #include <ctime> using namespace std; bool isEvenA(int n) { if(n%2==0) return true; return false; } void isEvenB(int n) { if(n%2==0) throw 0; } int main() { clock_t start=clock(); int result=0; for(int i=0;i<2000000000;++i) { #if 1 if(isEvenA(i)) ++result; #else try{ isEvenB(i); } catch(int){ ++result; } #endif } clock_t end=clock(); cout<<(end-start)/double(CLOCKS_PER_SEC)<<'\n'; cout<<result<<'\n'; return 1; }mit return: 2.36 Sekunden
mit throw: 2 Stunden 22 Minuten und 18 Sekunden
Warum muß ich gerade an java denken?
-
volkard schrieb:
mit return: 2.36 Sekunden
mit throw: 2 Stunden 22 Minuten und 18 SekundenIs dir langweilig?
-
volkard schrieb:
Warum muß ich gerade an java denken?
xD das trifft es jedoch sehr gut
-
Naja, Volkard, in deinem Beispiel treten ja auch andauernd throws auf, ich werfe ja nur Exceptions wenn was wirklich nicht geht. Und bei find() benutze ich auch kein Exceptions.

Mein Programm macht durchschnittlich 400-500 Calls zu ReadProcessMemory pro Sekunde. Wenn die Funktion fehl schlägt wird eine Exception geworfen. Aber es ist halt echt wichtig dass meine Daten so schnell wie möglich geupdated werden, deswegen will ich keine Performance-Probleme dabei (Es wird ja auch noch mehr gemacht als die MemoryReads

So habe ich es zum Beispiel gelöst:
template <typename R> R readMemory(DWORD_PTR address) { DWORD bytesRead; R returnedBytes; bool ec; ec = ReadProcessMemory(processHandle_, reinterpret_cast<LPVOID>(address), reinterpret_cast<LPVOID>(&returnedBytes), sizeof(R), &bytesRead); if(!ec || bytesRead < sizeof(R)) throw std::runtime_error("Process::readMemory() Error : ReadProcessMemory() failed"); return returnedBytes; }Wenn die Exception geworfen wird, wird das Programm angehalten damit nicht mit falschen bzw veralteten Daten gearbeitet wird, eine Fehlermeldung ausgegeben, das Prozesshandle geschlossen + neues geöffnet und das Programm läuft weiter. Ohne Exceptions müsste ich entweder prüfen ob der Rückgabewert sinnvoll ist (was fast unmöglich ist) oder eben über ein Referenz das Ergebnis übermitteln und in der Return-Value ... blubbblubb.
Ich bin mit der Lösung eigentlich ziemlich glücklich, finde gerade den Try-Block deutlich schöner als das permanente Return-Value-Checking aber man wollte mir die ganze Zeit einreden dass man allein schon durch das Benutzen des Try-Blocks Performancesenkungen eingeht.
-
Icematix schrieb:
Ich bin mit der Lösung eigentlich ziemlich glücklich, finde gerade den Try-Block deutlich schöner als das permanente Return-Value-Checking aber man wollte mir die ganze Zeit einreden dass man allein schon durch das Benutzen des Try-Blocks Performancesenkungen eingeht.
Ein bisschen langsamer ist es schon, aber ReadProcessMemory wird wahrscheinlich 1000 mal mehr machen.
-
Mir war so, als sei das Exception-Handling von C++ auf die Geschwindigkeit im Normalfall optimiert, d.h. das einfache Durchlaufen eines try-Blockes sollte überhaupt nichts kosten, das Werfen und Fangen einer Exception kann dann aber ziemlich teuer werden.
Hab aber keine Quelle dafür, da müsste man bei den üblichen Verdächtigen suchen (Stroustrup, Meyers, ...).
-
Das Beispiel ist ganz ok.
Aber ich sehe nicht haufenweise try-Blöcke.
Allenthalben ein throw reinzubauen, ist oft doof. Viele packen sich die Bibliotheken mit throw voll, um logisch Fehler abzufangen, wo assert viel passender wäre. Aber bei Dir ist der throw-Grund kein Programmierfehler, sondern ein selten anzutreffender Außenweltfehler.
Dagegen hat keiner was. Du erzähltest von "eigentlich in jeder Funktion ein try", und das kann eigentlich nicht sein. Man wirft, woimmer es nötig ist, aber fangen tut man nur in der main(), in der ThreadFunc() und gelegentlich woanders, wenn man sinnvoll neuaufsetzen kann, wie bei Dir un dem Prozessneugelade.
-
Bashar schrieb:
Mir war so, als sei das Exception-Handling von C++ auf die Geschwindigkeit im Normalfall optimiert, d.h. das einfache Durchlaufen eines try-Blockes sollte überhaupt nichts kosten, das Werfen und Fangen einer Exception kann dann aber ziemlich teuer werden.
Hab aber keine Quelle dafür, da müsste man bei den üblichen Verdächtigen suchen (Stroustrup, Meyers, ...).Diesmal bei gcc. Die haben (wenigstens mingw) angeblich neulich umgestellt, daß die Exceptions im Normalfall keine Laufzeit mehr kosten, dafür im Wuffall noch ein Bißchen langsamer sind.
-
bool isEven(int n) { if(n==-12430) throw 0; return n%2==0; } int main() { clock_t start=clock(); int result=0; #if 1 try{ for(int i=0;i<2000000000;++i) { if( isEven(i) ) ++result; } } catch(int){ std::cerr<<"OMG"; } #else for(int i=0;i<2000000000;++i) { try{ if( isEven(i) ) ++result; } catch(int){ std::cerr<<"OMG"; } } #endif clock_t end=clock(); cout<<(end-start)/double(CLOCKS_PER_SEC)<<'\n'; cout<<result<<'\n'; return 1; }6.078 vs. 6.828
-
VS 2008
-
messerschneider schrieb:
6.078 vs. 6.828
Das entspricht dann genau einem Taktzyklus je Schleifendurchgang, bei 2.67 GHz. Da stellt sich dann schon die Frage, ob das wirklich die allgemeinen Kosten von try sind, oder ob der Unterschied hier nicht einfach am Code im Speziellen liegt. Immerhin ist der Schleifenkörper im Falle des inneren try-catch komplexer.
-
Als Faustregel gilt für mich, dass ein Programm im Idealfall auch ohne Exceptions ablaufen können muss. Desweiteren erlaube ich mir das werfen von Exceptions nur in Ausnahmefällen (schliesslich ist der Name bestimmt nicht zufällig gewählt...), doch dort spare ich damit nicht. Wichtig ist es doch, dass man nicht überall alle Exceptions zu fangen versucht. Tritt irgendwo eine Exception auf, dann kocht diese dann auch ziemlich weit hoch, bis sie behandelt und sichtbar wird...
-
Wie alle Features so kann man auch Exceptions mißbrauchen. Dazu gibt es sogar ein Anti-Pattern: http://en.wikipedia.org/wiki/Expection_handling
-
throw new SuccessException("Data saved successfully");
