Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it faster to break rather than to raise an exception?

Tags:

After checking a few simple tests, it seems as if it might be faster to break from a loop to end a generator rather than to raise a StopIteration exception. Why is this the case if the standard and accepted method of stopping a generator is using the exception. source

In [1]: def f():
   ....:     for i in range(1024):
   ....:         yield None
   ....:         break
   ....:     

In [2]: def g():
   ....:     for i in range(1024):
   ....:         yield None
   ....:         raise StopIteration
   ....:     

In [3]: %timeit for i in f(): pass
1000000 loops, best of 3: 1.22 µs per loop

In [4]: %timeit for i in g(): pass
100000 loops, best of 3: 5.9 µs per loop

In [5]: %timeit for i in f(): pass
1000000 loops, best of 3: 1.22 µs per loop

In [6]: %timeit for i in g(): pass
100000 loops, best of 3: 5.82 µs per loop
like image 209
Tankobot Avatar asked May 11 '16 06:05

Tankobot


People also ask

What is the difference between raising an exception and handling an exception?

except is how you handle an exception that some other code signalled. raise is how you signal an exception yourself. It's like asking what the difference is between making a phone call and answering the phone. In except you usually handle exceptions, you normally don't raise other exceptions.

What does it mean to raise an exception?

Raising an exception is a technique for interrupting the normal flow of execution in a program, signaling that some exceptional circumstance has arisen, and returning directly to an enclosing part of the program that was designated to react to that circumstance.

What is the difference between raising an exception and handling an exception Python?

raise allows you to throw an exception at any time. assert enables you to verify if a certain condition is met and throw an exception if it isn't. In the try clause, all statements are executed until an exception is encountered. except is used to catch and handle the exception(s) that are encountered in the try clause.

How do I raise an exception without stopping the program?

To print an exception without exiting the program, use a try/except block and assign the exception object to variable e using except Exception as e . Now, call print(e) in the except branch to print a simple error message.


2 Answers

Why is this the case if the standard and accepted method of stopping a generator is using the exception.

The exception StopIteration is raised only when the generator has nothing to produce any more. And, it is not a standard way of stopping a generator midway.

Here are two statements from the documentation on generators about how to stop them properly:

  1. PEP 479 -- Change StopIteration handling inside generators:

... the proposal also clears up the confusion about how to terminate a generator: the proper way is return , not raise StopIteration.

  1. PEP 255 -- Simple Generators

Q. Why allow "return" at all? Why not force termination to be spelled "raise StopIteration"?

A. The mechanics of StopIteration are low-level details, much like the mechanics of IndexError in Python 2.1: the implementation needs to do something well-defined under the covers, and Python exposes these mechanisms for advanced users. That's not an argument for forcing everyone to work at that level, though. "return" means "I'm done" in any kind of function, and that's easy to explain and to use. Note that "return" isn't always equivalent to "raise StopIteration" in try/except construct, either (see the "Specification: Return" section).

So the correct way would be to use a return statement instead of using break or raise StopIteration.


it seems as if it might be faster to break from a loop to end a generator rather than to raise a StopIteration exception.

Indeed it is because when raising the exception there is more job to do. You can use the dis module to get a look at the bytecode:

In [37]: dis.dis(f)
  2           0 SETUP_LOOP              26 (to 29)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (1024)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                12 (to 28)
             16 STORE_FAST               0 (i)

  3          19 LOAD_CONST               0 (None)
             22 YIELD_VALUE         
             23 POP_TOP             

  4          24 BREAK_LOOP          
             25 JUMP_ABSOLUTE           13
        >>   28 POP_BLOCK           
        >>   29 LOAD_CONST               0 (None)
             32 RETURN_VALUE        

In [38]: dis.dis(g)
  2           0 SETUP_LOOP              31 (to 34)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (1024)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                17 (to 33)
             16 STORE_FAST               0 (i)

  3          19 LOAD_CONST               0 (None)
             22 YIELD_VALUE         
             23 POP_TOP             

  4          24 LOAD_GLOBAL              2 (StopIteration)
             27 RAISE_VARARGS            1
             30 JUMP_ABSOLUTE           13
        >>   33 POP_BLOCK           
        >>   34 LOAD_CONST               0 (None)
             37 RETURN_VALUE

You can see that almost everything is same but for raising the exception, it has to execute some extra instructions:

24 LOAD_GLOBAL              2 (StopIteration)
27 RAISE_VARARGS            1
like image 111
AKS Avatar answered Jan 03 '23 18:01

AKS


If you do a break then it just exits the loop and in your case there is nothing more to do, so it also exits the function. If you raise an exception, then it still has to handle all the exception stuff that is going on in the background.

more work == more execution time

The reason StopIteration may be used (I have never used that one, so don't quote me on that) is to catch it later, or to simply exit a nested loop. I just had a quick look at the source you gave and it seems to me that StopIteration was only used to illustrate how generators work, but it was not used with a generator.

I hope that answers your question.

like image 44
Matthias Schreiber Avatar answered Jan 03 '23 16:01

Matthias Schreiber