Python’s Benchmarking via timeit

Python’s timeit module can be used both from the CLI and programmatically:

Command-line:

bash $ python3 -m timeit -s "import dot; a = [1]*1000000; b = [2]*1000000" "dot.dotProductLoop(a, b)"

Pros:

  • Easy one-liner for ad hoc benchmarks.

Cons:

  • Hard to reuse code.
  • Clunky for complex setups.
  • Involves spawning new processes.

Programmatic:

python import timeit

a = [1] * 1000000
b = [2] * 1000000
N = 10

time = timeit.timeit("dot.dotProductLoop(a, b)", globals=globals(), number=N)
print(time / N)

Can use autorange():

python timer = timeit.Timer("dot.dotProductLoop(a, b)", globals=globals())
num, total = timer.autorange()

Can use repeat():

python print(timeit.repeat("dot.dotProductLoop(a, b)", globals=globals(), number=N, repeat=5))

But repeat() lacks automatic range selection, and autorange() lacks repetition.


🛠️ The Solution: autobench() Utility

A custom function that:

  • Automatically determines iteration count.
  • Repeats multiple times.
  • Reports best per-loop timing like Go.
python def autobench(stmt, globals=None, repeat=5):
timer = timeit.Timer(stmt, globals=globals)
num, _ = timer.autorange()
raw_timings = timer.repeat(repeat=repeat, number=num)
best = min(raw_timings)
print(f"{num} loops, best of {repeat}: {best/num:.3f}s per loop")

Usage:

python autobench("dot.dotProductLoop(a, b)", globals=globals())

Example output:

arduino 10 loops, best of 5: 0.021s per loop

đź§© Limitations & Improvements

  • No automatic unit scaling (ns, µs, ms, etc.).
  • repeat + autorange isn’t directly supported by timeit, hence the need for this wrapper.

📚 TL;DR

While Python’s timeit module provides the necessary primitives, they aren’t ergonomically composed. This autobench() function effectively mimics Go’s seamless benchmark experience within Python scripts — ideal for integrating into regular codebases and CI flows without resorting to shell scripts or external runners.

Previous Post
Next Post