Python's timeit module provides accurate timing of small code snippets. It runs code multiple times, accounts for garbage collection, and gives reliable performance measurements.

Command Line Usage

# Time a simple expression
python -m timeit "sum(range(100))"
 
# Multiple statements
python -m timeit "x = [i**2 for i in range(100)]"
 
# Setup code
python -m timeit -s "import math" "math.sqrt(100)"
 
# Specify iterations
python -m timeit -n 1000000 "1 + 1"

Basic Python Usage

import timeit
 
# Time a simple statement
time = timeit.timeit('sum(range(100))', number=10000)
print(f"Total time: {time:.4f}s")
 
# Average per execution
avg = time / 10000
print(f"Average: {avg*1000:.4f}ms")

With Setup Code

import timeit
 
setup = """
import random
data = [random.randint(0, 1000) for _ in range(1000)]
"""
 
# Time sorting
time = timeit.timeit('sorted(data)', setup=setup, number=1000)
print(f"Sorting 1000 elements: {time/1000*1000:.3f}ms")

Timing Functions

import timeit
 
def method_one():
    return [i**2 for i in range(100)]
 
def method_two():
    return list(map(lambda x: x**2, range(100)))
 
# Time each function
t1 = timeit.timeit(method_one, number=10000)
t2 = timeit.timeit(method_two, number=10000)
 
print(f"List comprehension: {t1:.4f}s")
print(f"Map + lambda: {t2:.4f}s")

repeat() for Better Statistics

import timeit
 
times = timeit.repeat(
    'sum(range(100))',
    number=10000,
    repeat=5
)
 
print(f"Times: {times}")
print(f"Best: {min(times):.4f}s")
print(f"Average: {sum(times)/len(times):.4f}s")

Use min() of repeated runs—it's most representative (less affected by system load).

Timer Class

import timeit
 
# Create reusable timer
timer = timeit.Timer(
    stmt='"-".join(str(n) for n in range(100))',
    setup='pass'
)
 
# Run once (includes setup)
single = timer.timeit(number=1)
 
# Run multiple times
total = timer.timeit(number=1000)
 
# Auto-calibrate number of runs
number, total_time = timer.autorange()
print(f"{number} iterations in {total_time:.2f}s")

Comparing Approaches

import timeit
 
# String concatenation methods
approaches = {
    'join': '"-".join(words)',
    'plus': 'result = ""; result += w for w in words',
    'format': '"-".join(f"{w}" for w in words)',
}
 
setup = 'words = ["hello", "world", "python", "test"]'
 
for name, stmt in approaches.items():
    time = timeit.timeit(stmt, setup, number=100000)
    print(f"{name}: {time:.4f}s")

Using globals()

Access current namespace:

import timeit
 
my_list = list(range(1000))
 
def search_list():
    return 500 in my_list
 
# Pass globals to access my_list and search_list
time = timeit.timeit(search_list, globals=globals(), number=10000)
print(f"Time: {time:.4f}s")

Real Example: List vs Set Lookup

import timeit
 
setup = """
my_list = list(range(10000))
my_set = set(range(10000))
target = 5000
"""
 
list_time = timeit.timeit('target in my_list', setup, number=1000)
set_time = timeit.timeit('target in my_set', setup, number=1000)
 
print(f"List lookup: {list_time:.4f}s")
print(f"Set lookup: {set_time:.4f}s")
print(f"Set is {list_time/set_time:.0f}x faster")

Timing Context Manager

Create your own for quick profiling:

import timeit
 
class Timer:
    def __enter__(self):
        self.start = timeit.default_timer()
        return self
    
    def __exit__(self, *args):
        self.elapsed = timeit.default_timer() - self.start
 
# Usage
with Timer() as t:
    result = sum(range(1000000))
 
print(f"Elapsed: {t.elapsed:.4f}s")

Best Practices

  1. Use enough iterations: Small code needs many runs
  2. Take the minimum: min(timeit.repeat(...)) is most reliable
  3. Disable GC carefully: Only for tight loops without allocation
  4. Compare fairly: Same setup, same number of iterations
  5. Profile first: timeit for micro-benchmarks, cProfile for overall performance

Disabling Garbage Collection

For very precise measurements:

import timeit
 
# GC is disabled during timing by default
# To include GC overhead:
timer = timeit.Timer(
    'list(range(1000))',
    'gc.enable()',
    globals={'gc': __import__('gc')}
)

Quick One-Liner

import timeit
 
# Fastest way to time something
print(timeit.timeit(lambda: sum(range(100)), number=10000))

Summary

timeit is the go-to for reliable micro-benchmarks:

  • Runs code multiple times for statistical significance
  • Disables garbage collection during timing
  • Works from command line or Python code
  • Use repeat() and take min() for best results

For profiling entire programs, use cProfile. For quick timing, timeit gets the job done.

React to this post: