In most programming scenarios, it is very easy to re-invent the wheel especially when you are working with high-level languages like Python.  For example, in a scenario where you want to measure the execution time of a block of code like a function, you might hastily  turn to time.time() or other similar functions. But there is a module that is designed specifically for this purpose, it is called timeit

The timeit module defines some useful function for measuring the execution time of small code snippets. It makes it easy to benchmark small blocks of code , making it even easier to optimize performance by comparing different implementations.

Unlike other manual approaches, timeit automatically handles multiple repetitions and platform-independent timing.

timeit is a standard library module and you don't need to perform any extra installations, you just need to import it in your code.

ExampleEdit & Run
import timeit

print(timeit)
Output:
<module 'timeit' from '/usr/local/lib/python3.11/timeit.py'> [Finished in 0.0372154610000166s]

Basic usage of timeit

For basic usage, you will use the following timeit functions:

  1. timeit.timeit()
  2. timeit.repeat()

using timeit.timeit() 

The timeit.timeit() function runs a given statement multiple times and returns the total execution time.

The basic syntax is as shown below:

timeit.timeit(stmt, number)

 Where:

  • stmt is the statement/code to be timed, it is either a callable(usually a function) or a string.
  • number is how many times to repeat the statement, it defaults to 1000,000 times.
ExampleEdit & Run
import timeit

def example(): 
    L = [i for i in range(100)] #a simple operation

exec_time = timeit.timeit( example, number=1000)

print(exec_time)
Output:
0.0042856700000015735 [Finished in 0.01998036999998476s]

Using timeit.repeat() 

This function repeats the timing process multiple times and returns a list of results.

The basic syntax is as follows:

timeit.repeat(stmt, number, repeat)

Where repeat is the number of repetitions, it defaults to 5 times

ExampleEdit & Run
import timeit

def example():
    L = [i for i in range(100)]

exec_time = timeit.repeat( example, number=1000, repeat=3 )

print(exec_time)
Output:
[0.00429941999999528, 0.004307789999984379, 0.0042650799999819355] [Finished in 0.029782301000011557s]

Technically, the timeit.repeat() function runs the code using timeit.timeit() the specified number of times and appends the results in a list.

timeit with string statements

In the previous examples, we called timeit functions with a callable/function argument. It is also possible to pass a code snippet as a string.

ExampleEdit & Run
import timeit

exec_time = timeit.timeit( '[i for i in range(100)]', number=1000 )

print(exec_time)
Output:
0.004146260000027269 [Finished in 0.019715850000011415s]

setup code

You can add setup code to contextualize the execution by specifying the setup parameter.

ExampleEdit & Run
import timeit

setup_code = '''import random
X=[i for i in range(100)]
random.shuffle(X)'''

exec_time = timeit.timeit( 'X.sort()', setup=setup_code )
print( exec_time )
Output:
0.4225469600000338 [Finished in 0.45338764100000617s]

Running timeit from the Command Line

You can use timeit in terminals too. You will simply pass your code as a command-line argument:

python -m timeit "[i for i in range(100)]"

You can specify the parameters as follows:

  • -n, number
  • -r, repeats
​python -m timeit -n 1000 -r 3  "[i for i in range(100)]" 

Class based timing

Class-based timing with timeit.Timer offers even more control. You can persist setup between runs or change parameters dynamically.

The Timer class constructor takes the same basic parameters as the timeit.timeit() function.

ExampleEdit & Run
from timeit import Timer

t = Timer(stmt='x.sort()', 
          setup='import random; x = [random.random() for _ in range(1000)]')
print(t)
Output:
<timeit.Timer object at 0x7effe6d86fd0> [Finished in 0.016153430999963803s]

The created timer object has three key methods:

  • timeit(number=1000000) - Single timing run.
  • repeat(repeat=5, number=1000000) - Multiple timing runs

  • autorange() - Automatically determine optimal iterations

Let us wrap up by implementing a more practical example of comparing the performance of three sorting approaches, i.e sorted(x), x.sort() and Heap sort.

ExampleEdit & Run
from timeit import Timer
import random

# Setup code that creates test data
setup = '''
import random
from heapq import heappush, heappop
data = [random.random() for _ in range(5000)]
'''

# Define different sorting approaches
sort_methods = {
    'Built-in sorted': 'sorted(data)',
    'List sort method': 'data.copy().sort()',
    'Heap sort': '''
heap = []
for x in data:
    heappush(heap, x)
sorted_data = [heappop(heap) for _ in range(len(heap))]
    '''
}

# Benchmark each method
results = {}
for name, stmt in sort_methods.items():
    t = Timer(stmt=stmt, setup=setup)
    # Run 7 trials of 100 iterations each
    times = t.repeat(repeat=7, number=100)
    avg_time = sum(times) / len(times)
    results[name] = avg_time

print("Sorting Method Benchmark (5000 elements):")
for name, time in sorted(results.items(), key=lambda x: x[1]):
    print(f"{name:20}: {time:.5f} sec per 100 sorts")
Output:
Sorting Method Benchmark (5000 elements): Built-in sorted : 0.05468 sec per 100 sorts List sort method : 0.05724 sec per 100 sorts Heap sort : 0.25175 sec per 100 sorts [Finished in 2.580171140999994s]