Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is an inline if/else faster than a .get() in Python?

I was reviewing some code earlier and the developer wrote an inline if/else rather than a get() to retrieve an element from a list if it exists (otherwise give it a default value). I decided to spring some timeit code on repl and was pretty confused by the result. The if/else takes 1/3 the time of the get().

Here is the repl code, and below is the code in the repl as well as the result for posterity:

import timeit

D = {"a": 1, "b": 2, "c": 3}

def ef(): return D['a'] if 'a' in D else 1

def gt(): return D.get('a', 1)

print "gt1", timeit.timeit(gt, number=10000)
print "ef1", timeit.timeit(ef, number=10000)
print "ef2", timeit.timeit(ef, number=10000)
print "gt2", timeit.timeit(gt, number=10000)

and the results:

gt1 0.0659999847412
ef1 0.0239999294281
ef2 0.0249998569489
gt2 0.0539999008179

and a visual of 10 iterations of the above timeit calls, where the result has been multiplied by 10000 for representation purposes

visual of 10 iterations

like image 863
jsanc623 Avatar asked Feb 13 '15 16:02

jsanc623


1 Answers

The D.get() path includes an attribute lookup, and a method call:

>>> import dis
>>> D = {"a": 1, "b": 2, "c": 3}
>>> def gt(): return D.get('a', 1)
... 
>>> dis.dis(gt)
  1           0 LOAD_GLOBAL              0 (D)
              3 LOAD_ATTR                1 (get)
              6 LOAD_CONST               1 ('a')
              9 LOAD_CONST               2 (1)
             12 CALL_FUNCTION            2
             15 RETURN_VALUE        

The attribute lookup (LOAD_ATTR) especially slows things down.

If you remove the attribute lookup (and give the in test a local to work with), the field is evened out:

>>> def gt_fast(D_get=D.get): return D_get('a', 1)
... 
>>> def ef_fast(D=D): return D['a'] if 'a' in D else 1
... 
>>> timeit.timeit(gt_fast)
0.2174091339111328
>>> timeit.timeit(ef_fast)
0.2139298915863037
like image 178
Martijn Pieters Avatar answered Oct 19 '22 00:10

Martijn Pieters