Specifying number loops in timeit (whether %timeit in Jupyter or timeit.timeit() in scripts) is essential for getting accurate, low-noise performance measurements. By controlling the number of executions per run (-n / number) and the number of independent timing repetitions (-r / repeat), you balance precision, runtime, and stability — especially important for micro-benchmarks (small snippets) or noisy environments (system load, caching, JIT warmup). In 2026, tuning these parameters is standard practice for reliable benchmarking, optimization, regression testing, and meeting latency/throughput goals in production code.
Here’s a complete, practical guide to specifying loops and runs in timeit: syntax, how to choose values, real-world examples, interpreting results, and modern best practices for accurate and actionable timing.
In Jupyter/IPython notebooks, use %timeit flags to control loops and runs — timeit auto-tunes by default, but explicit control gives consistency across machines and code sizes.
# Auto-tuned (default) — good for quick checks
%timeit sum(range(1000))
# 1.45 µs ± 55.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# Explicit: 100 loops per run, repeat 5 times — more stable for medium snippets
%timeit -n 100 -r 5 sum(range(1000))
# 1.42 µs ± 12.3 ns per loop (mean ± std. dev. of 5 runs, 100 loops each)
# Very fast snippet: many loops, fewer runs
%timeit -n 1000000 -r 3 x = 1 + 2
# 12.3 ns ± 0.45 ns per loop (mean ± std. dev. of 3 runs, 1,000,000 loops each)
In regular scripts, use timeit.timeit() with number (executions per measurement) and repeat (independent timing runs) — timeit returns total time; divide by number for per-call average.
import timeit
def my_sum(n):
return sum(range(n))
# 10,000 executions per run, repeat measurement 7 times
total_time = timeit.timeit(lambda: my_sum(1000), number=10000, repeat=7)
avg_per_call = total_time / (10000 * 7) * 1e6 # µs
print(f"Average: {avg_per_call:.2f} µs per call")
print(f"Total time across all runs: {total_time:.4f} s")
Real-world pattern: comparing implementations or tuning parameters — specify loops/runs to get stable, comparable numbers across machines and code versions.
# Compare sum vs manual loop — 1000 runs, 1000 loops each
data = list(range(100_000))
t1 = timeit.timeit(lambda: sum(data), number=1000, repeat=5)
print(f"sum(): {t1 / (1000 * 5) * 1e6:.2f} µs avg")
def loop_sum(lst):
total = 0
for x in lst:
total += x
return total
t2 = timeit.timeit(lambda: loop_sum(data), number=1000, repeat=5)
print(f"loop: {t2 / (1000 * 5) * 1e6:.2f} µs avg")
# sum(): 1.23 µs avg
# loop: 8.45 µs avg
# ? sum() is ~7× faster — stable across repeats
Best practices make timeit results accurate, comparable, and actionable. For micro-benchmarks (small snippets <1µs), use high number (100k–1M) and moderate repeat (5–10) — timeit auto-tunes well but explicit values improve consistency. For slower code (>1ms), use lower number (100–1000) and higher repeat (7–20) — captures system noise. Always time the hot path — use setup for one-time init (data creation, imports). Disable GC with timeit(..., gc=False) if needed — timeit disables it by default for accuracy. Modern tip: use time.perf_counter() for manual high-resolution timing — timeit uses it internally. Visualize results — plot mean ± std dev vs input size with matplotlib/seaborn for scaling behavior. In production, integrate timeit into tests/CI — assert avg < threshold, track regressions over commits. Combine with profilers — timeit shows total time; cProfile, line_profiler, or py-spy show where time is spent. Prefer generators over lists for large data — sum(x**2 for x in range(n)) avoids list creation. Avoid timing in debug mode — use release builds, disable assertions, and warm up JIT if using PyPy/Numba.
Specifying number loops and runs in timeit turns noisy measurements into reliable benchmarks — mean gives speed, std dev shows stability, repeats give confidence. In 2026, control -n/number and -r/repeat, time the hot path, profile bottlenecks, and log metrics. Master timeit tuning, and you’ll get trustworthy numbers — the foundation of fast, scalable Python code.
Next time you want to know how fast your code really is — use timeit with explicit loops and runs. It’s Python’s cleanest way to measure performance accurately and reproducibly.