try...catch slooowness?

Steven Schveighoffer schveiguy at yahoo.com
Mon Dec 20 09:29:29 PST 2010


On Sun, 19 Dec 2010 07:33:29 -0500, spir <denis.spir at gmail.com> wrote:

> Hello,
>
>
> I had not initially noticed that the 'in' operator (for AAs) returns a  
> pointer to the looked up element. So that, to avoid double lookup in  
> cases where lookups may fail, I naively used try...catch. In cases of  
> very numerous lookups, my code suddenly became blitz fast. So that I  
> wondered about exception handling efficiency. Below a test case (on my  
> computer, both loops run in about the same average time):
>
> void main () {
>     byte[uint] table = [3:1, 33:1, 333:1];
>     byte b;
>     byte* p;
>     Time t0;
>     uint N1 = 246, N2 = 9999999;
>    // try...catch
>     t0 = time();
>     foreach (n ; 0..N1) {
>         try b = table[n];
>         catch (RangeError e) {}
>     }
>     writefln("try...catch version time: %sms", time() - t0);
>    // pointer
>     t0 = time();
>     foreach (n ; 0..N2) {
>         p = (n in table);
>         if (p) b = table[n];
>     }
>     writefln("pointer version time: %sms", time() - t0);
>    writefln("pointer version is about %s times faster",N2/N1);
> }
> ==>
> try...catch version time: 387ms
> pointer version time: 388ms
> pointer version is about 40650 times faster
>
> Note that both versions perform a single lookup trial; the difference  
> thus only lies in pointer deref vs try...catch handling, i guess. What  
> do you think?

This example is misleading.  First, catching an exception should be a rare  
occurrence (literally, an exception to the rule).  You are testing the  
case where catching an exception vastly outweighs the cases where an  
exception is not thrown.  What I'm saying is, catching an exception is  
very slow, but *trying* to catch an exception is not.

Second, exception handling is not meant to be used in the way you used  
it.  You don't use it as an extra return value.  I'd expect a more  
reasonable use of catching an exception in AAs as this:

try
{
   foreach(n ; 0..N1)
   {
      b = table[n];
   }
}
catch(RangeError e)
{
    writeln("Caught exception! ", e);
}

An exception is a recoverable error, but it usually means something is  
wrong, not 'business as usual'.  This doesn't mean it's impossible to  
design poor interfaces that use exceptions for everything, but it  
shouldn't be that way.  An exception should always be a rare occurrence,  
when something happens that you don't expect.  A huge clue that you are  
using exceptions poorly or that the interface is not meant to be used that  
way is if your exception handling is being done at the innermost level of  
your program.  Exception handling is great when it exists at a much higher  
level, because you can essentially do all error handling in one spot, and  
simply write code without worrying about error codes.

This is why the 'in' operator exists for AAs.

General rules of thumb for AAs:

1. if you expect that a value is always going to be present when you ask  
for it, use exception handling at a high level.
2. if you *don't* expect that, and want to check the existence of an  
element, use 'in'

Now, after saying all that, improving how exception handling works can  
only be good.  So comparing exception handling performance in D to  
exception handling in other languages can give a better idea of how well  
D's exception handling performs.

-Steve


More information about the Digitalmars-d mailing list