State and timing

The State object exposes the parts of benchmark::State that Python users need. Most benchmarks only use iteration:

for _ in state:
    do_work()

Batched iteration for very fast bodies

Each for _ in state step crosses the Python→C boundary via __next__. For sub-100 ns bodies that dispatch is a meaningful share of the measured time. State.batches(n) yields n once per batch, letting the inner loop run as pure-Python for _ in range(n):

@mew.benchmark
def bench_tight(state):
    a, b = 1, 2
    for n in state.batches(1024):
        for _ in range(n):
            a + b

Warning

Batched and naive (for _ in state) timings are not directly comparable — the batched form removes per-iter dispatch overhead from the measurement. Pick one style per benchmark and don’t switch back and forth across releases. A consistent tags=("batched",) is a good way to flag these runs for downstream comparison.

The last batch may overshoot max_iterations by up to n - 1 body executions. GB reports the actual iteration count and divides by it, so the per-iter time is accurate — just expect a slightly higher total wall time when the body has visible side effects. Keep n well below max_iterations (1024 is a reasonable default).

Pausing the timer

State.pause() is a context manager that excludes its body from the measured region. Use it when you need to rebuild state every iteration, but don’t want the cost to leak into the timing:

import random

@mew.benchmark
def bench_shuffle_then_sort(state):
    for _ in state:
        with state.pause():
            data = list(range(1000))
            random.shuffle(data)
        sorted(data)

The pause() context manager keeps the timer in a consistent state even when the body raises.

Repetitions vs iterations

  • Iterations are the inner loop count chosen by Google Benchmark to reach min_time. Forcing this disables auto-tuning — only do it when comparing against an absolute baseline.

  • Repetitions rerun the entire benchmark, including warm-up, N times so you get _mean, _median, _stddev aggregate rows.

Combine repetitions=10 with report_aggregates_only=True if you only care about the aggregates and want the per-rep rows hidden from output sinks.

Real vs. CPU time

Reporters print both Real and CPU columns. The difference is informative on its own:

  • Real == CPU: single-threaded, CPU-bound work.

  • Real > CPU: the benchmark is sleeping, blocking on I/O, or contending on a lock.

  • Real < CPU: multi-threaded — pair with measure_process_cpu_time=True if you want CPU time summed across threads.

Set use_real_time=True if your benchmark’s primary metric is wall-clock time.

Manual time

Some benchmarks need their own clock — e.g. GPU work where the CPU launches the kernel and waits asynchronously. Set use_manual_time=True and call the state’s manual-time setter inside the loop. See the Google Benchmark documentation for the exact contract.