This is a strange story.
One morning, I woke up and wrote a few lines of code. Then, there came an interesting situation. I need to assign two elements of an array to two variables. There are many ways of achieving this in python.
If you're a javascript developer, your instinct should say this is the way.
const [x,y] = items;
Suppose, I've got an array [1,2]
and I need to assign two variables to this value. Something like follow:
a = 1
b = 2
I did the natural thing I was used to doing, unpacking the variable.
a, b = items
This works with charm. But suddenly I remember of an another way. What if I index the array and grab the elements that way.
a, b = items[0], items[1]
Or even.
a = items[0]
b = items[1]
Now, I want to profile this code. I know this is over-engineering at max but let's see what exactly happens.
First I wrote the same implementation in python and disassembled the code.
In [15]: def x():
...: a , b = items
...: return
...:
In [16]: def y():
...: a = items[0]
...: b = items[1]
...: return
...:
In [17]: dis(x)
2 0 LOAD_GLOBAL 0 (items)
2 UNPACK_SEQUENCE 2
4 STORE_FAST 0 (a)
6 STORE_FAST 1 (b)
3 8 LOAD_CONST 0 (None)
10 RETURN_VALUE
In [18]: dis(y)
2 0 LOAD_GLOBAL 0 (items)
2 LOAD_CONST 1 (0)
4 BINARY_SUBSCR
6 STORE_FAST 0 (a)
3 8 LOAD_GLOBAL 0 (items)
10 LOAD_CONST 2 (1)
12 BINARY_SUBSCR
14 STORE_FAST 1 (b)
4 16 LOAD_CONST 0 (None)
18 RETURN_VALUE
In [19]:
Do you see the extra overhead when I index the value? First, it need to load the constant i.e. index value, then perform the subscription. The store the value in a
. This is a long way of doing it.
If I look at the other code, which is unpacking. One instruction UNPACK_SEQUENCE
is capable of returning all the values and it's followed by two store instructions.
Even by intuition, I suppose the first function (x)
will run faster.
Now, let's compare the execution time.
In [19]: %timeit x()
84.3 ns ± 3.59 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [20]: %timeit y()
108 ns ± 2.42 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
To no surprise, the unpacking beats the indexing by around ~24ns. This isn't the kind of optimization you should be thinking when writing a code. Write a clean, readable testable code.
Don't abuse your codebase with lines like the following:
a ,= items
# use this
b = items[0]