Python
timeit
performance testing
code timing
benchmarking

How can I time a code segment for testing performance with Pythons timeit?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

timeit is Python's standard tool for measuring small code snippets repeatedly with less noise than a one-off time.time() call. It is designed for micro-benchmarking, which means the real skill is not only calling it, but setting up the code so the numbers actually mean something.

The Simplest Programmatic Use

At its most basic, timeit.timeit() runs a statement many times and returns the total elapsed time.

python
1import timeit
2
3elapsed = timeit.timeit("sum(range(100))", number=10000)
4print(elapsed)

This tells you how long the statement took across 10000 runs.

If you want an average per run, divide by number:

python
average = elapsed / 10000
print(average)

Prefer Callables Over Long String Snippets

timeit historically used strings, but for real code it is often clearer to benchmark a callable.

python
1import timeit
2
3
4def compute():
5    return sum(range(100))
6
7elapsed = timeit.timeit(compute, number=10000)
8print(elapsed)

This avoids awkward string quoting and makes the benchmark easier to refactor.

Use repeat() Instead of Trusting One Number

One timing run can be noisy because of system activity, cache effects, or CPU scheduling. repeat() gives several runs so you can inspect the spread.

python
1import timeit
2
3
4def compute():
5    return sum(range(100))
6
7results = timeit.repeat(compute, repeat=5, number=10000)
8print(results)
9print("best:", min(results))

For micro-benchmarks, the minimum value is often the most useful signal because it best approximates execution without unrelated system interference.

Benchmark the Code You Actually Care About

A common mistake is timing setup work instead of the target operation. Keep setup outside the timed function when possible.

Bad example:

python
def slow_test():
    data = list(range(10000))
    return sum(data)

If you only care about summing, moving list construction outside the timed section is more accurate:

python
1data = list(range(10000))
2
3def sum_only():
4    return sum(data)

timeit can only measure what you give it, so benchmark scope matters.

Use the globals Argument for Real Variables

When timing strings, pass real names through globals instead of rebuilding context in the snippet.

python
1import timeit
2
3data = list(range(10000))
4
5elapsed = timeit.timeit("sum(data)", number=10000, globals=globals())
6print(elapsed)

This is much cleaner than trying to recreate data in the stmt or setup strings.

Command-Line and REPL Usage

timeit also works from the command line:

bash
python -m timeit "sum(range(100))"

If you use IPython or Jupyter, %timeit is often the easiest interface:

python
%timeit sum(range(100))

That is convenient for exploratory work, but the standard library API remains better for scripted, repeatable benchmarks.

Understand What timeit Is Good For

timeit is ideal for:

  • comparing small implementations
  • checking whether one expression is faster than another
  • measuring lightweight functions repeatedly

It is not ideal for:

  • full application benchmarking
  • network or database timing with highly variable latency
  • performance analysis needing CPU profiles or memory traces

For those cases, profiling tools or end-to-end performance measurements are better.

Keep Results Honest

Micro-benchmarks are easy to misuse. A good benchmark should:

  • avoid unrelated setup inside the timed code
  • run enough iterations to smooth timer resolution noise
  • compare equivalent work
  • be repeated several times
  • be interpreted in context

A benchmark that shows a 5% difference in a function called once per hour is usually not actionable.

timeit Disables Some Noise Sources, Not All of Them

timeit helps by choosing a suitable timer and reducing some benchmarking mistakes. It does not guarantee perfect experimental conditions. CPU scaling, background load, memory state, and interpreter warm-up effects can still influence results.

That is why repeat() and cautious interpretation matter.

Common Pitfalls

  • Timing setup work instead of the actual code path you care about.
  • Trusting a single measurement rather than several repeated runs.
  • Benchmarking string snippets that rebuild objects every time unintentionally.
  • Using timeit for workloads dominated by I/O or network latency.
  • Treating tiny micro-benchmark wins as meaningful without checking the real application impact.

Summary

  • Use timeit to measure small Python code segments repeatedly and with less noise than ad hoc timing.
  • Prefer callables or globals-aware snippets over large embedded strings.
  • Use repeat() and inspect the best or stable runs instead of one timing result.
  • Keep setup outside the measured code when possible.
  • Use timeit for micro-benchmarks, not as a substitute for full application profiling.

Course illustration
Course illustration

All Rights Reserved.