Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 2: Why is floor division operator faster than normal division operator?

Consider the following Python 2 code

from timeit import default_timer

def floor():
    for _ in xrange(10**7):
        1 * 12 // 39 * 2 // 39 * 23 - 234

def normal():
    for _ in xrange(10**7):
        1 * 12 / 39 * 2 / 39 * 23 - 234

t1 = default_timer()
floor()
t2 = default_timer()
normal()
t3 = default_timer()

print 'Floor  %.3f' % (t2 - t1)
print 'Normal %.3f' % (t3 - t2)

And the output, on my computer, is

Floor  0.254
Normal 1.766

So, why is the floor division operator // faster than the normal division operator / when both of them are doing the same thing?

like image 316
avamsi Avatar asked Nov 17 '25 12:11

avamsi


1 Answers

The Python interpreter is pre-calculating the expression inside the loop in floor, but not in normal.

Here's the code for floor:

>>> dis.dis(floor)

  5           0 SETUP_LOOP              24 (to 27)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               9 (10000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                10 (to 26)
             16 STORE_FAST               0 (_)

  6          19 LOAD_CONST              15 (-234)
             22 POP_TOP             
             23 JUMP_ABSOLUTE           13
        >>   26 POP_BLOCK           
        >>   27 LOAD_CONST               0 (None)
             30 RETURN_VALUE        

You can see that the expression is already calculated LOAD_CONST 15 (-234).

Here's the same for normal:

>>> dis.dis(normal)

  9           0 SETUP_LOOP              44 (to 47)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               9 (10000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                30 (to 46)
             16 STORE_FAST               0 (_)

 10          19 LOAD_CONST              10 (12)
             22 LOAD_CONST               5 (39)
             25 BINARY_DIVIDE       
             26 LOAD_CONST               6 (2)
             29 BINARY_MULTIPLY     
             30 LOAD_CONST               5 (39)
             33 BINARY_DIVIDE       
             34 LOAD_CONST               7 (23)
             37 BINARY_MULTIPLY     
             38 LOAD_CONST               8 (234)
             41 BINARY_SUBTRACT     
             42 POP_TOP             
             43 JUMP_ABSOLUTE           13
        >>   46 POP_BLOCK           
        >>   47 LOAD_CONST               0 (None)
             50 RETURN_VALUE        

This time, the calculation is only partially simplified (eg: the initial 1 * is omitted), and most of the operations are performed at runtime.

It looks like Python 2.7 doesn't do constant folding containing the ambiguous / operator (that may be integer or float division depending on its operands). Adding from __future__ import division at the top of the program causes the constant to be folded in normal just as it was in floor (although the result is different of course, since now / is float division).

normal
 10           0 SETUP_LOOP              24 (to 27)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               9 (10000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                10 (to 26)
             16 STORE_FAST               0 (_)

 11          19 LOAD_CONST              15 (-233.6370808678501)
             22 POP_TOP             
             23 JUMP_ABSOLUTE           13
        >>   26 POP_BLOCK           
        >>   27 LOAD_CONST               0 (None)
             30 RETURN_VALUE        

It's not like the interpreter couldn't do the constant folding with the default / operator, but it doesn't. Perhaps the code was back-ported from Python 3, and it wasn't considered important to make it work with the ambiguous division operator.

like image 68
Paul Hankin Avatar answered Nov 19 '25 01:11

Paul Hankin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!