Why is the ternary operator faster than .get for dicts?

I recently discovered something unexpected. Given a dict d that does not contain the key k , using a three-dimensional operator to try to get the default return item:

 >>> def tern(): ... d[k] if k in d else 'foo' ... >>> timeit.timeit(tern, number=1000000) 0.12342095375061035 

faster than dict .get() function:

 >>> def get_meth(): ... d.get(k, 'foo') ... >>> timeit.timeit(get_meth, number=1000000) 0.20549297332763672 

It seems intriguing to me. I would think that the ternary operator would require 2 searches via dict (once to check k in d ), then another to get d[k] , while .get would just try to extract d[k] , and if it fails, return 'foo' .

I used this for both the large dict (one million elements) and the small (100), and both times the triple was significantly faster. What is going on behind the scenes here?

+5
source share
1 answer

If you parse the two methods, you will see that get has an extra CALL_FUNCTION , which is expensive in python compared to the POP_JUMP_IF_FALSE .

, if in

  3 0 LOAD_CONST 1 ('blub') 3 LOAD_GLOBAL 0 (d) 6 COMPARE_OP 6 (in) 9 POP_JUMP_IF_FALSE 22 12 LOAD_GLOBAL 0 (d) 15 LOAD_CONST 1 ('blub') 18 BINARY_SUBSCR 19 JUMP_FORWARD 3 (to 25) >> 22 LOAD_CONST 2 ('foo') >> 25 POP_TOP 26 LOAD_CONST 0 (None) 29 RETURN_VALUE 

Production Method:

  6 0 LOAD_GLOBAL 0 (d) 3 LOAD_ATTR 1 (get) 6 LOAD_CONST 1 ('blub') 9 LOAD_CONST 2 ('foo') 12 CALL_FUNCTION 2 #Expensive call 15 POP_TOP 16 LOAD_CONST 0 (None) 19 RETURN_VALUE 

There is a very long article that I read some time ago that has a section that describes why CALL_FUNCTION is so expensive: https://doughellmann.com/blog/2012/11/12/the-performance-impact-of -using-dict-instead-of-in-cpython-2-7-2 /

+3
source

Source: https://habr.com/ru/post/1274237/


All Articles