140

In another question, the accepted answer suggested replacing a (very cheap) if statement in Python code with a try/except block to improve performance.

Coding style issues aside, and assuming that the exception is never triggered, how much difference does it make (performance-wise) to have an exception handler, versus not having one, versus having a compare-to-zero if-statement?

Community
  • 1
  • 1
Thilo
  • 257,207
  • 101
  • 511
  • 656

5 Answers5

151

Why don't you measure it using the timeit module? That way you can see whether it's relevant to your application.

OK, so I've just tried the following (using Python 3.11.1 on Windows 11):

import timeit

statements=["""\
try:
    b = 10/a
except ZeroDivisionError:
    pass""",
"""\
if a:
    b = 10/a""",
"b = 10/a"]

for a in (1,0):
    for s in statements:
        t = timeit.Timer(stmt=s, setup='a={}'.format(a))
        print("a = {}\n{}".format(a,s))
        print("%.2f usec/pass\n" % (1000000 * t.timeit(number=100000)/100000))

Result:

a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.06 usec/pass

a = 1
if a:
    b = 10/a
0.05 usec/pass

a = 1
b = 10/a
0.03 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.27 usec/pass

a = 0
if a:
    b = 10/a
0.02 usec/pass

a = 0
b = 10/a
Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
  File "C:\Python311\Lib\timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<timeit-src>", line 6, in inner
ZeroDivisionError: division by zero

As you can see, there is not much of a difference between using a try/except clause vs. an explicit if statement, unless the exception gets triggered. (And of course, not having any control structure is fastest, though not by much, and it will crash the program if anything goes wrong).

Compare this to the results obtained in 2010:

a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.25 usec/pass

a = 1
if a:
    b = 10/a
0.29 usec/pass

a = 1
b = 10/a
0.22 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.57 usec/pass

a = 0
if a:
    b = 10/a
0.04 usec/pass

a = 0
b = 10/a
ZeroDivisionError: int division or modulo by zero

I appears that the PC I'm using now is about twice as fast as the one I had back then. The cost of handling an Exception appears identical, and the "normal" operations (arithmetic) have been improved even more than the handling of control structures, but the point from all those years ago still stands:

It's all within the same order of magnitude and unlikely to matter either way. Only if the condition is actually met (often), then the if version is significantly faster.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • 9
    Interesting. So `try/except` is faster than `if a != 0` – Thilo Mar 26 '10 at 09:15
  • 23
    Ahh, a fine choice of words: "it's all within the same order of magnitude"... I suspect many people who avoid exceptions do so expecting them to be 10x as slow. – Garrett Bluma Nov 06 '11 at 20:16
  • 1
    Running your code on my Fedora with python 2.7.5 shows that the "if" version(0.08 usec/pass) is faster than the "try/except" one(0.11 usec/pass) when a=1. – duleshi May 09 '14 at 04:12
  • 1
    @duleshi Interesting. I wonder if it's a x86/x64 thing? Or perhaps different processor extensions? – Basic Aug 09 '16 at 19:49
  • 1
    Hmm they are *not*, by definition, within the same order of magnitude, since the runtime of the `try` version is more than 10 times slower than the `if` version. Whether 50us is too high a cost to pay is another question. – lesurp Nov 23 '22 at 10:44
  • 1
    This answer is useful but now very old, I wondered about comparable timings now. Updated result (Dell Latitude 5410, Windows 10, Python 3.8): cost of a triggered except block is now 0.26us. So that's about half what it was, for all the other cases it's more like a quarter of what it was in 2010. – Ben Taylor Feb 01 '23 at 17:03
82

This question is actually answered in the Design and History FAQ:

A try/except block is extremely efficient if no exceptions are raised. Actually catching an exception is expensive.

Smi
  • 13,850
  • 9
  • 56
  • 64
Michael
  • 11,612
  • 10
  • 41
  • 43
  • 8
    I was just wondering how efficient "extremely efficient" is. Apparently it is faster then even a very simple "if" statement. – Thilo Mar 28 '10 at 01:40
  • The excerpt you posted is from the [Design and History FAQ](http://docs.python.org/faq/design.html). – nitsas Jan 22 '12 at 01:22
  • Maybe "extremely efficient" means [something like what is done in Java](https://stackoverflow.com/a/141652/4710226)? – ebk Oct 29 '18 at 05:41
25

In Python 3.11,

“Zero-cost” exceptions are implemented. The cost of try statements is almost eliminated when no exception is raised. (Contributed by Mark Shannon in bpo-40222.)

https://docs.python.org/3.11/whatsnew/3.11.html#optimizations

SuperNova
  • 25,512
  • 7
  • 93
  • 64
20

This question is misleading. If you assume the exception is never triggered, neither one is optimal code.

If you assume the exception is triggered as part of an error condition, you are already outside the realm of wanting optimal code (and you probably aren't handling it at a fine-grained level like that anyway).

If you are using the exception as part of the standard control flow - which is the Pythonic "ask forgiveness, not permission" way - then the exception is going to be triggered, and the cost depends on the kind of exception, the kind of if, and what percentage of time you estimate the exception happens.

7
Q: Is try/catch costly in python?

Should i be concerned when I use try catch? In what way?

This is just a summary of the answers already given.

A: When there is an exception if is much faster. Otherwise no.

@SuperNova writes that exceptions are at zero cost so it is faster than having an if-statement when no exception. However, handling exceptions is costly so:

Use try for things that can fail. If possible, avoid try for things you know will fail

Example:
  1. Good case, use try:
try:
    x = getdata() # an external function 
except:
    print('failed. Retrying')
  1. Bad case, here if-version is preferred:
y = f(x) # f never fails but often returns 0
try:
    z = 1 / y # this fails often
except:
    print('failed.')

# if-version
y = f(x)
if y != 0:
    z = 1 / y
else:
    print('failed.')

Hunaphu
  • 589
  • 10
  • 11