5.3. Optimization Timeit

5.3.1. Timeit - Programmatic use

  • timeit

Code 5.1. Timeit simple statement
from timeit import timeit


setup = """name = 'Alice'"""
stmt = """result = f'Hello {name}'"""

duration = timeit(stmt, setup, number=10000)

print(duration)
# 0.0005737080000000061
Code 5.2. Timeit multiple statements with setup code
from timeit import timeit


setup = """
firstname = 'Alice'
lastname = 'Apricot'
"""

TEST = dict()
TEST[0] = 'name = f"{firstname} {lastname}"'
TEST[1] = 'name = "{0} {1}".format(firstname, lastname)'
TEST[2] = 'name = firstname + " " + lastname'
TEST[3] = 'name = " ".join([firstname, lastname])'


for stmt in TEST.values():
    duration = timeit(stmt, setup, number=10000)
    print(f'{duration:.5}\t{stmt}')

# 0.00071559    name = f"{firstname} {lastname}"
# 0.0026514     name = "{0} {1}".format(firstname, lastname)
# 0.001015      name = firstname + " " + lastname
# 0.0013494     name = " ".join([firstname, lastname])
Code 5.3. Timeit with globals()
from timeit import timeit


def factorial(n: int) -> int:
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)


duration = timeit(
    stmt='factorial(500); factorial(400); factorial(450)',
    globals=globals(),
    number=10000,
)

duration = round(duration, 6)

print(f'factorial time: {duration} seconds')
# factorial time: 2.845382 seconds

5.3.2. Timeit - Console use

Code 5.4. Timeit
python3 -m timeit -n100000 -r100 --setup='name="Alice"' 'output = f"Hello {name}"'
# 100000 loops, best of 100: 55.9 nsec per loop

python3 -m timeit -n100000 -r100 --setup='name="Alice"' 'output = "Hello {name}".format(name=name)'
# 100000 loops, best of 100: 327 nsec per loop

python3 -m timeit -n100000 -r100 --setup='name="Alice"' 'output = "Hello %s" % name'
# 100000 loops, best of 100: 124 nsec per loop
-n N, --number=N
how many times to execute 'statement'

-r N, --repeat=N
how many times to repeat the timer (default 5)

-s S, --setup=S
statement to be executed once initially (default pass)

-p, --process
measure process time, not wallclock time, using time.process_time() instead of time.perf_counter(), which is the default

-u, --unit=U
specify a time unit for timer output; can select nsec, usec, msec, or sec

-v, --verbose
print raw timing results; repeat for more digits precision

-h, --help
print a short usage message and exit

5.3.3. Assignments

# FIXME: Write tests

# %% About
# - Name: Memoization
# - Difficulty: easy
# - Lines: 5
# - Minutes: 5

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Create empty `dict` named `CACHE`
# 2. In the dictionary, we will store the results of calculating
#    the function for the given parameters:
#    - key: function argument
#    - value: calculation result
# 3. Modify function `factorialA(n: int)`
# 4. Before running the function, check if the result has already
#    been calculated:
#    - if so, return data from `CACHE`
#    - if not, calculate, update `CACHE` and return the value
# 5. Compare the speed of operation
# 6. Run doctests - all must succeed

# %% Polish
# 1. Stwórz pusty `dict` o nazwie `CACHE`
# 2. W słowniku będziemy przechowywali wyniki wyliczenia funkcji
#    dla zadanych parametrów:
#    - klucz: argument funkcji
#    - wartość: wynik obliczeń
# 3. Zmodyfikuj funkcję `factorialA(n: int)`
# 4. Przed uruchomieniem funkcji, sprawdź czy wynik został już
#    wcześniej obliczony:
#    - jeżeli tak, to zwraca dane z `CACHE`
#    - jeżeli nie, to oblicza, aktualizuje `CACHE` i zwraca wartość
# 5. Porównaj prędkość działania
# 6. Uruchom doctesty - wszystkie muszą się powieść

# %% Doctests
"""
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`

# %% Imports
from timeit import timeit
import sys

# %% Types

# %% Data
sys.setrecursionlimit(5000)


# %% Result
def factorialA(n: int) -> int:
    return 1 if n==0 else n*factorialA(n-1)


# Do not modify anything below
def factorialB(n: int) -> int:
    return 1 if n==0 else n*factorialB(n-1)


durationA = timeit(
    stmt='factorialA(50); factorialA(40); factorialA(45); factorialA(35)',
    globals={'factorialA': factorialA},
    number=10000,
)

durationB = timeit(
    stmt='factorialB(50); factorialB(40); factorialB(45); factorialB(35)',
    globals={'factorialB': factorialB},
    number=10000,
)

times_faster = durationB / durationA
print(f'factorialA time: {durationA:.4f} seconds')
print(f'factorialB time: {durationB:.4f} seconds')
print(f'Cached solution is {times_faster:.0f} times faster')
# factorialA time: 0.0654 seconds
# factorialB time: 0.0658 seconds
# Cached solution is 1 times faster