This plays with optimizing a fibonacci generator function for speed.
Study loop unrolling.
In [1]:
from itertools import islice
First we start with straightforward fibonacci generator function.
In [2]:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
In [3]:
n = 45
In [4]:
known_good_output = tuple(islice(fibonacci(), n))
# known_good_output
In [5]:
%timeit sum(islice(fibonacci(), n))
Next, we unroll the loop. Note that there are no assignments that just move things around. There is no wasted motion inside the loop. It reminds me of the musical round Three Blind Mice.
In [6]:
def fibonacci():
a, b = 0, 1
while True:
yield a
c = a + b
yield b
a = b + c
yield c
b = c + a
assert(known_good_output == tuple(islice(fibonacci(), n)))
In [7]:
%timeit sum(islice(fibonacci(), n))
Next, we unroll the loop more and more to see if that makes the generator faster.
In [8]:
def fibonacci():
a, b = 0, 1
while True:
yield a
c = a + b
yield b
a = b + c
yield c
b = c + a
yield a
c = a + b
yield b
a = b + c
yield c
b = c + a
assert(known_good_output == tuple(islice(fibonacci(), n)))
In [9]:
%timeit sum(islice(fibonacci(), n))
In [10]:
def fibonacci():
a, b = 0, 1
yield a
yield b
while True:
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
assert(known_good_output == tuple(islice(fibonacci(), n)))
In [11]:
%timeit sum(islice(fibonacci(), n))
In [12]:
def fibonacci():
a, b = 0, 1
yield a
yield b
while True:
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
assert(known_good_output == tuple(islice(fibonacci(), n)))
In [13]:
%timeit sum(islice(fibonacci(), n))
In [14]:
def fibonacci():
a, b = 0, 1
yield a
yield b
while True:
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
assert(known_good_output == tuple(islice(fibonacci(), n)))
In [15]:
%timeit sum(islice(fibonacci(), n))
In [16]:
def fibonacci():
a, b = 0, 1
yield a
yield b
while True:
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
c = a + b
yield c
a = b + c
yield a
b = c + a
yield b
assert(known_good_output == tuple(islice(fibonacci(), n)))
In [17]:
%timeit sum(islice(fibonacci(), n))
I get significantly different results each time I run the cells in the notebook, so it is unclear how much loop unrolling is good.