11.18. Faster Python#

  • For CPU intensive work

    • Doing calculations,manipulations etc

      • Use multiprocessing (not supported in Jupyter)

  • For IO related work

    • Reading from files/databases/querying large datasets

      • Use multithreading

  • For basic caching

    • Use @lru_cache decorator from functools module

    • With the @lru_cache decorator in place, you store every call and answer in memory to access later if requested again. But how many calls can you save before running out of memory?

      • Python’s @lru_cache decorator offers a maxsize attribute that defines the maximum number of entries before the cache starts evicting old items. By default, maxsize is set to 128. If you set maxsize to None, then the cache will grow indefinitely, and no entries will be ever evicted. This could become a problem if you’re storing a large number of different calls in memory

11.18.1. Example#

11.18.1.1. Without Caching#

import time
from functools import lru_cache

def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)


start=time.time()
print([fib(n) for n in range(20)])
end=time.time()
print(end-start)
# with caching,took 0.01s
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
0.007303953170776367

11.18.1.2. With Caching#

import time
from functools import lru_cache

@lru_cache(maxsize=32)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)


start=time.time()
print([fib(n) for n in range(20)])
end=time.time()
print(end-start)

# with caching,took 0s
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
0.0001468658447265625