Re: try/except heavy to use ?

Guido.van.Rossum@cwi.nl
Tue, 16 Aug 1994 13:05:38 +0200

> I was rather surprised by trying the following piece of code:
>
[...]
>
> Function t1 and t2 both add 1 + 2 + ... + 99 ( = 4950 ), using two
> methods: a is a dictionary {10: 1, 2: 20, ..., 990: 99}. t1 uses
> a.has_key() to see if a has the right key (success: 1/10), t2 uses
> a try/except statement to do the same test.
>
> Here are the result of this program on a Sparc 10/SunOS 4.1.3:
>
> 4950
> ncalls tottime percall cumtime percall filename:lineno(function)
> 1 0.017 0.017 0.100 0.100 <string>:0(?)
> 1 0.083 0.083 0.083 0.083 test.py:4(t1)
> 4950
> ncalls tottime percall cumtime percall filename:lineno(function)
> 1 1.033 1.033 1.033 1.033 test.py:11(t2)
> 1 0.000 0.000 1.033 1.033 <string>:0(?)
>
> The function using try/except takes more than 10 times the function using
> has_key.
>
> Does it mean that we shouldn't use try/except statements except in a few
> special cases ? For example, shouldn't we never use string.index but
> string.find instead ? (string.find doesn't raise an exception)

Yes, a try/except that usually fails and then immediately caught is
not a good idea in an inner loop. The cost is not so much in the
setup code for the except handler, but in the extra processing that
happens when the exception is raised and handled: When it is raised
(the failing a[i] operation), the exception type (a prebuilt object)
is stored, an error message is converted from C string to Python
string object and stored, and a traceback object (a small structure
pointing to the current stack frame) is allocated. When it is handled
(the except clause), these three values are stored in sys.exc_type,
sys.exc_value and sys.exc_traceback.

On the other hand, if you have a situation where the exception is
rarely raised (say in 5% of the cases), the code using has_key() does
twice as many dictionary lookups as the code using try/except. It
would be interesting to measure the break-even point. I just tried
this with a trivial variation of Samuel's program and it appears that
if there are more than 7% misses in the dictionary the try/except
version is slower. However, with no misses, the try/except version is
more than twice as fast! So the try/except setup time is negligeable.
(This was with a 1000 entry dictionary on an SGI R4000 Indigo. As
they say, "your mileage may vary.")

One conclusion is that in the next version of the strop, there better
be an implementation of find and rfind -- currently string.find calls
strop.index and catches the exception, it were better to do this the
other way around!

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>
<URL:http://www.cwi.nl/cwi/people/Guido.van.Rossum.html>

PS The program you showed can't have produced the output you showed;
since it only iterates over range(100) it would actually only have
added 0..9 producing 45 :-) Changing that to 1000 and making 'a'
global (so the profiler can find it) yielded results like you
reported.