Python if vs try-except

I was wondering why try-except is slower than if in the program below.

def tryway(): try: while True: alist.pop() except IndexError: pass def ifway(): while True: if alist == []: break else: alist.pop() if __name__=='__main__': from timeit import Timer alist = range(1000) print "Testing Try" tr = Timer("tryway()","from __main__ import tryway") print tr.timeit() print "Testing If" ir = Timer("ifway()","from __main__ import ifway") print ir.timeit() 

The results that I get are interesting.

 Testing Try 2.91111302376 Testing If 0.30621099472 

Can anyone shed some light on why the attempt is much slower?

+20
performance python
Oct. 14 '10 at 3:21
source share
7 answers

You install alist only once. The first call to tryway clears it, then each subsequent call does nothing.

 def tryway(): alist = range(1000) try: while True: alist.pop() except IndexError: pass def ifway(): alist = range(1000) while True: if alist == []: break else: alist.pop() if __name__=='__main__': from timeit import Timer print "Testing Try" tr = Timer("tryway()","from __main__ import tryway") print tr.timeit(10000) print "Testing If" ir = Timer("ifway()","from __main__ import ifway") print ir.timeit(10000) >>> Testing Try >>> 2.09539294243 >>> Testing If >>> 2.84440898895 
+37
Oct 14 '10 at 3:33
source share

Exception handling in most languages ​​is usually slow. Most compilers, interpreters, and virtual machines (which support exception handling) consider exceptions (language idiom) as exceptions (unusual). Performance optimization involves trade-offs and the quick implementation of exceptions will usually mean that other areas of the language will suffer (either in performance or in the simplicity of the design).

On a more technical level, exceptions usually mean that the VM / interpreter (or the execution runtime library) needs to save a bunch of state and start pulling all the state in the function call stack (called unwinding) until the point where a valid catch (except) is found.

Or, looking at it from a different point of view, the program stops working when an exception occurs and the "debugger" takes over. This debugger searches back through the stack (call function data) for a catch that matches the exception. If he finds one, he will clear things and return control to the program at that moment. If he does not find it, he returns control to the user (possibly in the form of an interactive debugger or REPL).

+4
Oct 14 2018-10-10T00:
source share

If you are really interested in speed, both of your opponents could lose weight.

while True: slower than while 1: - True is a global "variable" that is loaded and tested; 1 is a constant, and the compiler runs the test and emits an unconditional jump.

while True: redundant. Add while / if / break together: while alist != []:

while alist != []: - slow way to write while alist:

Try the following:

 def tryway2(): alist = range(1000) try: while 1: alist.pop() except IndexError: pass def ifway2(): alist = range(1000) while alist: alist.pop() 

`

+3
Oct 14 '10 at 3:59
source share

There is an even faster way to repeat, although sometimes we want the list to be physically evaded, so we know how much is left. Then alist should be the parameter for the generator. (John is also suitable for while alist: . I put the function with the generator and the list used (ifway ()), etc., Therefore, the values ​​are valid from the function (not even used):

 def tryway(): alist = range(1000) try: while True: yield alist.pop() except IndexError: pass def whileway(): alist = range(1000) while alist: yield alist.pop() def forway(): alist = range(1000) for item in alist: yield item if __name__=='__main__': from timeit import Timer print "Testing Try" tr = Timer("list(tryway())","from __main__ import tryway") print tr.timeit(10000) print "Testing while" ir = Timer("list(whileway())","from __main__ import whileway") print ir.timeit(10000) print "Testing for" ir = Timer("list(forway())","from __main__ import forway") print ir.timeit(10000) J:\test>speedtest4.py Testing Try 6.52174983133 Testing while 5.08004508953 Testing for 2.14167694497 
+2
Oct 14 2018-10-10
source share

Not sure, but I think this is something like this: while true matches the usual line of instructions, which means that the processor can conveyorly do all kinds of nice things. Exceptions go straight through all this, so the VM needs to be handled specifically, and it takes time.

0
Oct 14 '10 at 3:25
source share

Just thought of throwing this into the mix: I tried the following script, which seems to assume that exception handling is slower than processing the else :

 import time n = 10000000 l = range(0, n) t0 = time.time() for i in l: try: i[0] except: pass t1 = time.time() for i in l: if type(i) == list(): print(i) else: pass t2 = time.time() print(t1-t0) print(t2-t1) 

gives:

 5.5908801555633545 3.512694835662842 

So, despite the fact that I know that someone is likely to comment on the use of time rather than timeit ), it seems that the use of try / except in loop is slowed by 60%. Thus, it might be better to go with if/else after going through a for loop of several billion elements.

0
Aug 05 '17 at 6:41
source share

defensive programming requires that one test for rare and / or abnormal conditions, some of which will not occur within a year or many years, so in these circumstances an attempt is possible - in addition, it can be justified.

-one
Jul 26 '17 at 5:38 on
source share