www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Benchmark of try/catch

reply bearophile <bearophileHUGS lycos.com> writes:
Part of the timings, seconds:
N = 4_000_000:
  C:       0.36
  Java:    5.59
  C++:     9.97
  Psyco:  29.28
  Python: 32.68
  D:      48.76  SLOW=false
  D:      88.45  SLOW=true

You can find it here, with C/D/Java/C++/Python code too:
http://leonardo-m.livejournal.com/78890.html

I'd like to see timing comparisons on Linux between a Java reference and LDC.

And I'd also like to know why using printf changes the situation so much.

Bye,
bearophile
Mar 23 2009
next sibling parent TomD <t_demmer nospam.web.de> writes:
bearophile Wrote:

[...]
 And I'd also like to know why using printf changes the situation so much.
 

is that printf pulls in stdarg, whereas writfln doesn't. Which means we replace one oddity with another. Ciao Tom D.
Mar 23 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
 From your site:

Having fast exceptions is getting more important, for example it's
important to have fast exceptions in Python because there they are
sometimes used to control flow too. For example to convert a string to
int/float the common Python idiom is to use a try/except.

I disagree. Exceptions shouldn't be abused as a second return value. The correct way would be to return an algebraic type. The return value would either contain the result of the function call, or an object that describes the error (like with exceptions). And actually, I wonder if exception handling really leads to better error handling. Maybe it makes it even _worse_, because the fact, that the function call can fail with an error (exception), isn't explicit part of a function signature anymore. It isn't really obvious anymore to the programmer, which exceptions might be thrown. Further, a programmer might get the wrong impression that errors are handled automagically and he doesn't really need to care. Sometimes I get the impression, that exceptions are so loved, because programmers think they can ignore error handling when using them. When I write C code, I know that ignoring return values can lead to horribly hard to locate bugs, and I force myself to check the return value (even if it doesn't seem important). This means I actually design some kind of failure path in my code. In D, the exception mechanism seems to move the actual error handling to somewhere "far away" (down below in the call stack). But actually, you end up never adding real error handling. Not even crappy error handling, like it's the case with most C code. Even more, to obtain the exception as a "second return value", you have to use that painful try-catch statement, which adds a lot of noise to your code. There's also that problem (I think Andrei mentioned it) that try-catch introduces a scope, and this interacts badly with auto and various other things. Checking a return value would be more straight forward. Using exceptions in a string->int conversion routine is really horrible and incredibly stupid. It raises all these issues, just because you can't signal failure in a better way. For parsing input, failure should be something to be _expected_, not an _exception_. You always want to check for success, but you don't force the programmer to check; instead, you'll make it harder by requiring the programmer to add this awkward try-catch thing around the function call. There are some more things one could criticize, and possible solutions one could discuss (like reviving checked exception in a non-sucky version), but I'll stop now. /rant
Mar 23 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
grauzone:

From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really horrible and
incredibly stupid.<

I agree that it's not nice looking, but in Python that's the standard idiom. In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions? Python3 also removes the find() method of strings, and leaves only the index() method, that is like find(), but raise ValueError when the substring is not found. So you are forced to use exceptions here too. So far in D I have used exceptions to control flow only once, in this library module (original code idea by Witold Baryluk, modified): http://www.fantascienza.net/leonardo/so/dlibs/generators.html Bye, bearophile
Mar 23 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
bearophile wrote:
 grauzone:
 
From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really horrible and
incredibly stupid.<

I agree that it's not nice looking, but in Python that's the standard idiom. In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions? Python3 also removes the find() method of strings, and leaves only the index() method, that is like find(), but raise ValueError when the substring is not found. So you are forced to use exceptions here too. So far in D I have used exceptions to control flow only once, in this library module (original code idea by Witold Baryluk, modified): http://www.fantascienza.net/leonardo/so/dlibs/generators.html Bye, bearophile

Andrei already solved this problem; I'm just waiting for its implementation to be possible in D... http://www.nwcpp.org/Meetings/2006/05.html :D -- Daniel
Mar 23 2009
next sibling parent grauzone <none example.net> writes:
Daniel Keep wrote:
 
 bearophile wrote:
 grauzone:

From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really horrible and
incredibly stupid.<

In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions? Python3 also removes the find() method of strings, and leaves only the index() method, that is like find(), but raise ValueError when the substring is not found. So you are forced to use exceptions here too. So far in D I have used exceptions to control flow only once, in this library module (original code idea by Witold Baryluk, modified): http://www.fantascienza.net/leonardo/so/dlibs/generators.html Bye, bearophile

Andrei already solved this problem; I'm just waiting for its implementation to be possible in D... http://www.nwcpp.org/Meetings/2006/05.html

I only had a short look, is it the NaN known from floating point types generalized to all types?
 :D
 
   -- Daniel

Mar 23 2009
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
 I only had a short look, is it the NaN known from floating point types
 generalized to all types?

Not quite; NaNs don't abort your program (ignoring signalling NaNs, of course.)
 Yay, The Power of None! That's what I was referring to when proposed
 nullable types:
 
 int? value1 = atoi("0");   // 0
 int? value2 = atoi("#!$"); // null

Not quite; that assumes null isn't a valid value. What if the function was "getInstance"? Here's how I understand it to work. Functions may return a Maybe!(T), which is either a T or None (note that None is not null), and is implicitly castable to T. Now, let's say we have this: Maybe!(int) toInt(string); Suppose we have the following: void main() { writef("Enter a number: "); string num_str = readline(); auto result = toInt(num_str); int num = result; writefln("You entered: %d", num); } Now, when the program asks for a number, we enter "42". The program works exactly as one would expect; no drama. The Maybe!(int) is transparently cast to an int. Next we try entering "get nodded" instead. toInt doesn't take kindly to this, and wants to throw an InsultingInputException. However, it doesn't actually throw it; it merely constructs the exception and returns it inside a None value. So in this case, result is a Maybe!(int) containing a None, wrapping an Exception. This is fine. It's not until the program tries to cast the result to an int that the exception is thrown. That is, the exception is only thrown at the moment we attempt to USE the result and find it's not valid. Alternately, we could do this: void main() { writef("Enter a number: "); string num_str = readline(); auto result = toInt(num_str); if( result == None ) { writefln("Error: \"%s\" is not a number.", num_str); } else { int num = result; writefln("You entered: %d", num); } } We can directly test a Maybe!(T) to see if it's None; this allows us to safely work out whether the function failed or not. The idea behind all this is to allow for exception-style programming where we don't care about testing for failures (but still want them to stop our program via an exception) OR C-style programming where we DO care about a particular function failing, and test the return values. The nice thing about this is that you either explicitly test for and handle the error, or it jumps out at you like a spider if you try to ignore it -- best of both worlds without the risk of missing a failure. -- Daniel
Mar 24 2009
parent Rainer Deyke <rainerd eldwood.com> writes:
Daniel Keep wrote:
 int? value1 = atoi("0");   // 0
 int? value2 = atoi("#!$"); // null

Not quite; that assumes null isn't a valid value. What if the function was "getInstance"?

T?? value = getInstance();
 Here's how I understand it to work.  Functions may return a Maybe!(T),
 which is either a T or None (note that None is not null), and is
 implicitly castable to T.

'T??' (aka 'Maybe!(Maybe!(T))') must be a valid type. In Haskell, a 'Maybe Maybe Integer' can be 'Nothing', 'Just Nothing', or 'Just Just 5'. In C++, given 'boost::optional<boost::optional<int> > o', you can have '!o', '!*o', or '**o == 5'. -- Rainer Deyke - rainerd eldwood.com
Mar 24 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
bearophile wrote:
 grauzone:
 
From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really horrible and
incredibly stupid.<

I agree that it's not nice looking, but in Python that's the standard idiom. In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions?

IMHO best would be something like this: (int|Error) toInt(char[] s); The return value would have a dynamic type of either int or Error. One could do all sorts of things with it, like explicitly checking for the type and read out the actual data, or implicit conversion with throwing an exception if the type is not the correct one. Error can be an object that contains an error message (like Exception). Alternatively, one could introduce nullable types: int? toInt(char[] s); The use of nullable value type int? would be a bit similar to object references in D. One can check it for null-ness, and using a null value raises a special exception. As a third way, one could simply return a tuple: (bool, int) toInt(char[] s); The bool value would tell if the string was successfully parsed. If it's true, the second item of the tuple contains the parsed value. But this is hardly better than the standard D solution, where you'd use an "out" parameter to return one of the two return values.
 Python3 also removes the find() method of strings, and leaves only the index()
method, that is like find(), but raise ValueError when the substring is not
found. So you are forced to use exceptions here too.

I'm shocked. That's beyond stupid. Didn't they consider ease of use? Code written like that must look like a saw blade (due to the alternating indentation of the try blocks). And as noisy. Now I'm sure they did think something when they made it like this. What am I missing?
 So far in D I have used exceptions to control flow only once, in this library
module (original code idea by Witold Baryluk, modified):
 http://www.fantascienza.net/leonardo/so/dlibs/generators.html
 
 Bye,
 bearophile

Mar 23 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 bearophile wrote:
 grauzone:

 From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really 
 horrible and incredibly stupid.<

I agree that it's not nice looking, but in Python that's the standard idiom. In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions?

IMHO best would be something like this: (int|Error) toInt(char[] s); The return value would have a dynamic type of either int or Error. One could do all sorts of things with it, like explicitly checking for the type and read out the actual data, or implicit conversion with throwing an exception if the type is not the correct one. Error can be an object that contains an error message (like Exception). Alternatively, one could introduce nullable types: int? toInt(char[] s); The use of nullable value type int? would be a bit similar to object references in D. One can check it for null-ness, and using a null value raises a special exception. As a third way, one could simply return a tuple: (bool, int) toInt(char[] s); The bool value would tell if the string was successfully parsed. If it's true, the second item of the tuple contains the parsed value. But this is hardly better than the standard D solution, where you'd use an "out" parameter to return one of the two return values.

In D as of today you can use: Algebraic!(int, Exception) toInt(in char[] s); or Tuple!(int, "result", bool, "succeeded") toInt(in char[] s); (See std.variant and std.typecons.) What Phobos does in the particular case of conversions is: string s = "12a"; auto i = to!int(s); // throws auto j = parse!int(s); // returns 12, advances s to "a" Andrei
Mar 23 2009
parent grauzone <none example.net> writes:
"It should be a native language feature."
Mar 23 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Sergey Gromov:

Different languages require different idioms, that's my strong opinion.<

I agree a lot, but it seems I need quite more than one year to learn most D idioms. I am just ignorant and willing to learn.
 One really important benchmark would be something like this:
 results
 D: 5.52s
 C++: 4.96s

I have tested the following D and C++ code (note that the C++ code is a single file now): // D code class TerminateException {} void do_stuff(int i) { if (i == 1_000_000_000) throw new TerminateException; } void main() { for (int i; ; i++) { try { do_stuff(i); } catch (TerminateException e) { break; } } } // C++ code struct TerminateException {}; void do_stuff(int i) { if (i == 1000000000) throw TerminateException(); } int main() { for (int i = 0; ; i++) { try { do_stuff(i); } catch (const TerminateException & e) { break; } } return 0; } My timings are: N = 1_000_000_000: C++: 0.06 s D: 5.10 s Compiled with: CPU: Core 2 at 2 GHz, 2 GB Ram, Windows XP. D code compiled with: DMD v1.041 -O -release -inline C++ code compiled with: gcc version 4.3.3-dw2-tdm-1 (GCC) -O3 -s ------------------------------- grauzone:
 Alternatively, one could introduce nullable types:
 int? toInt(char[] s);
 The use of nullable value type int? would be a bit similar to object
 references in D. One can check it for null-ness, and using a null value
 raises a special exception.
 As a third way, one could simply return a tuple:
 (bool, int) toInt(char[] s);

That's nice enough. The return value can be a struct (can contains the result plus a bool (or int) error flag/value).
Python3 also removes the find() method of strings, and leaves only the index()
method, that is like find(), but raise ValueError when the substring is not
found. So you are forced to use exceptions here too.<<


I'm shocked. That's beyond stupid. Didn't they consider ease of use?<

As usual you must know many details specific of that situation before commenting on a decision (I like the old -1 return value, but Python designers are often able to find design ideas better than my ones). Here are some discussions on the rationale: http://mail.python.org/pipermail/python-dev/2005-August/055704.html http://mail.python.org/pipermail/python-dev/2005-August/055705.html http://mail.python.org/pipermail/python-dev/2005-August/055708.html http://mail.python.org/pipermail/python-dev/2005-August/055711.html http://mail.python.org/pipermail/python-dev/2005-August/055712.html http://mail.python.org/pipermail/python-dev/2005-August/055717.html http://mail.python.org/pipermail/python-dev/2005-August/055740.html http://mail.python.org/pipermail/python-dev/2005-August/055748.html http://mail.python.org/pipermail/python-dev/2005-August/055754.html And the discussion doesn't stop there... Bye and thank you, bearophile
Mar 23 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
bearophile:
 N = 1_000_000_000:
   C++: 0.06 s
   D:   5.10 s

I think GCC compiles most things away, so the run time is constant. Bye, bearophile
Mar 23 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Mon, 23 Mar 2009 17:11:56 -0400, bearophile wrote:

 bearophile:
 N = 1_000_000_000:
   C++: 0.06 s
   D:   5.10 s

I think GCC compiles most things away, so the run time is constant.

Yes, I think it detected there was no side effects and eliminated the whole loop. That's why my original benchmark was in two modules.
Mar 23 2009
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Mon, 23 Mar 2009 09:07:16 -0400, bearophile wrote:

 grauzone:
 
From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really horrible and
incredibly stupid.<

I agree that it's not nice looking, but in Python that's the standard idiom. In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions? Python3 also removes the find() method of strings, and leaves only the index() method, that is like find(), but raise ValueError when the substring is not found. So you are forced to use exceptions here too. So far in D I have used exceptions to control flow only once, in this library module (original code idea by Witold Baryluk, modified): http://www.fantascienza.net/leonardo/so/dlibs/generators.html

D is designed to make normal execution flow fast and allow error handling be not-so-fast: http://www.digitalmars.com/d/2.0/errors.html so Python idioms simply do not fit. Different languages require different idioms, that's my strong opinion. One really important benchmark would be something like this: ********************* ********************* D exceptions -----main.d----------------- import worker; void main() { for (int i = 0;; i++) { try { do_stuff(i); } catch (TerminateException e) { break; } } } ------worker.d---------------- class TerminateException {} void do_stuff(int i) { if (i == 1_000_000_000) throw new TerminateException; } ----------------------- ******************* ******************* C++ exceptions ------main.cpp-------------------- #include "worker.h" int main() { for (int i = 0;; i++) { try { do_stuff(i); } catch (const TerminateException & e) { break; } } return 0; } ------worker.h-------------------- struct TerminateException {}; void do_stuff(int i); ------worker.cpp-------------------- #include "worker.h" void do_stuff(int i) { if (i == 1000000000) throw TerminateException(); } -------------------------- *************** *************** results D: 5.52s C++: 4.96s
Mar 23 2009
prev sibling next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from grauzone (none example.net)'s article
 Using exceptions in a string->int conversion routine is really horrible
 and incredibly stupid. It raises all these issues, just because you
 can't signal failure in a better way. For parsing input, failure should
 be something to be _expected_, not an _exception_. You always want to
 check for success, but you don't force the programmer to check; instead,
 you'll make it harder by requiring the programmer to add this awkward
 try-catch thing around the function call.
 There are some more things one could criticize, and possible solutions
 one could discuss (like reviving checked exception in a non-sucky
 version), but I'll stop now.
 /rant

For what it's worth, to me the whole point of exceptions is that they're for things that the immediate caller might not have a good answer for. The ideal test of whether you should be using exceptions or something else is whether it would be reasonable for the immediate caller of some function to ignore the error condition and let it bubble up. Checked exceptions defeat this because they require the caller of a function to do _something_ even if they can't actually do anything useful. In your string->int conversion example, it's perfectly reasonable that the caller of int() might have no idea what a reasonable course of action would be if the conversion fails and would want to pass the buck to its caller. Therefore, exceptions are a perfectly reasonable way to handle this. One thing that would be nice is if ddoc could automatically document what exceptions every function throws when generating documentation. Of course, there would be the problem of dealing with libraries for which the source code isn't available, but in a lot of cases, this would be feasible and very useful.
Mar 23 2009
parent Christopher Wright <dhasenan gmail.com> writes:
dsimcha wrote:
 In your string->int conversion example, it's
 perfectly reasonable that the caller of int() might have no idea what a
reasonable
 course of action would be if the conversion fails and would want to pass the
buck
 to its caller.  Therefore, exceptions are a perfectly reasonable way to handle
this.

C# provides things like int.Parse which throws on an invalid input and int.TryParse which takes an integer as an out parameter and returns true if the value was successfully parsed. It's slightly ugly to use TryParse, but most of the time you'll either provide a higher level handling mechanism (and so use int.Parse) or be dealing with sanitized input (and so use int.Parse).
 One thing that would be nice is if ddoc could automatically document what
 exceptions every function throws when generating documentation.  Of course,
there
 would be the problem of dealing with libraries for which the source code isn't
 available, but in a lot of cases, this would be feasible and very useful.

This could be done pretty simply, too -- use the type of the expression given with any "throw" statement.
Mar 23 2009
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 23 Mar 2009 16:32:03 +0300, Daniel Keep <daniel.keep.lists gmail.com>
wrote:

 bearophile wrote:
 grauzone:

 From your site:<

I don't own LiveJournal :-) That's just my blog, my site is elsewhere.
 Using exceptions in a string->int conversion routine is really  
 horrible and incredibly stupid.<

I agree that it's not nice looking, but in Python that's the standard idiom. In D I do the same thing when I want to know if a string contains an integer or float, with toInt/toFloat, how can I do it with no exceptions? Python3 also removes the find() method of strings, and leaves only the index() method, that is like find(), but raise ValueError when the substring is not found. So you are forced to use exceptions here too. So far in D I have used exceptions to control flow only once, in this library module (original code idea by Witold Baryluk, modified): http://www.fantascienza.net/leonardo/so/dlibs/generators.html Bye, bearophile

Andrei already solved this problem; I'm just waiting for its implementation to be possible in D... http://www.nwcpp.org/Meetings/2006/05.html :D -- Daniel

Yay, The Power of None! That's what I was referring to when proposed nullable types: int? value1 = atoi("0"); // 0 int? value2 = atoi("#!$"); // null
Mar 23 2009
prev sibling next sibling parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Mon, Mar 23, 2009 at 4:59 AM, bearophile <bearophileHUGS lycos.com> wrot=
e:
 Part of the timings, seconds:
 N =3D 4_000_000:
 =A0C: =A0 =A0 =A0 0.36
 =A0Java: =A0 =A05.59
 =A0C++: =A0 =A0 9.97
 =A0Psyco: =A029.28
 =A0Python: 32.68
 =A0D: =A0 =A0 =A048.76 =A0SLOW=3Dfalse
 =A0D: =A0 =A0 =A088.45 =A0SLOW=3Dtrue

From the D "Handling errors" page (http://www.digitalmars.com/d/1.0/errors.html): "Because errors are unusual, *execution of error handling code is not performance critical.*" What you're doing here is a blatant abuse of the exception system. It doesn't matter if it's a common idiom in Python.
Mar 23 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Jarrett Billingsley:
 What you're doing here is a blatant abuse of the exception system.

I am sorry. bearophile
Mar 23 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
bearophile wrote:
 Jarrett Billingsley:
 What you're doing here is a blatant abuse of the exception system.

I am sorry. bearophile

Don't apologize to Jarrett, apologize to the exception system.
Mar 23 2009
parent Christopher Wright <dhasenan gmail.com> writes:
davidl wrote:
 在 Tue, 24 Mar 2009 06:59:56 +0800,Christopher Wright 
 <dhasenan gmail.com> 写道:
 
 bearophile wrote:
 Jarrett Billingsley:
 What you're doing here is a blatant abuse of the exception system.

bearophile

Don't apologize to Jarrett, apologize to the exception system.

He's comparing two different things. The two exception systems are totally different. So he owes that apology.

Egads, man! Have you no sense of humor?
Mar 24 2009
prev sibling next sibling parent davidl <davidl 126.com> writes:
在 Mon, 23 Mar 2009 16:59:47 +0800,bearophile <bearophileHUGS lycos.com>  
写道:

 Part of the timings, seconds:
 N = 4_000_000:
   C:       0.36
   Java:    5.59
   C++:     9.97
   Psyco:  29.28
   Python: 32.68
   D:      48.76  SLOW=false
   D:      88.45  SLOW=true

 You can find it here, with C/D/Java/C++/Python code too:
 http://leonardo-m.livejournal.com/78890.html

 I'd like to see timing comparisons on Linux between a Java reference and  
 LDC.

 And I'd also like to know why using printf changes the situation so much.

 Bye,
 bearophile

Your C, C++ code won't catch exceptions: try { char*p; *p=0; } catch { } The mechanism of exception system is different. So the performance differs. In MSVC, you have __try / try , they are different things. The split of exception is quite uncomfortable. Though, it's quite fair tradeoff. You can't work out a better solution.
Mar 23 2009
prev sibling next sibling parent davidl <davidl 126.com> writes:
在 Tue, 24 Mar 2009 06:59:56 +0800,Christopher Wright  
<dhasenan gmail.com> 写道:

 bearophile wrote:
 Jarrett Billingsley:
 What you're doing here is a blatant abuse of the exception system.

bearophile

Don't apologize to Jarrett, apologize to the exception system.

He's comparing two different things. The two exception systems are totally different. So he owes that apology.
Mar 23 2009
prev sibling parent davidl <davidl 126.com> writes:
在 Tue, 24 Mar 2009 19:25:55 +0800,Christopher Wright  
<dhasenan gmail.com> 写道:

 davidl wrote:
 在 Tue, 24 Mar 2009 06:59:56 +0800,Christopher Wright  
 <dhasenan gmail.com> 写道:

 bearophile wrote:
 Jarrett Billingsley:
 What you're doing here is a blatant abuse of the exception system.

bearophile

Don't apologize to Jarrett, apologize to the exception system.

totally different. So he owes that apology.

Egads, man! Have you no sense of humor?

Sorry, for non-natives to catch native humor is tough :(
Mar 24 2009