Measure Python program execution time
Use perf_counter for elapsed time, timeit for repeated small benchmarks, and a timer that still reports when the measured code raises.
Measure elapsed execution time by subtracting two readings from time.perf_counter(). It is intended for durations and includes time spent waiting or sleeping. Wall-clock timestamps from time.time() answer a different question and can change when the system clock is adjusted.
Time a block of code
import time
start = time.perf_counter()
result = sum(range(1_000_000))
elapsed = time.perf_counter() - start
print(f"Elapsed: {elapsed:.6f} seconds")
The counter's absolute value has no calendar meaning. For integer nanosecond units, use perf_counter_ns() on Python 3.7+. That avoids floating-point representation loss; it does not guarantee nanosecond hardware accuracy. Inspect time.get_clock_info("perf_counter") for the clock's reported properties.
Know the limitation of time.time()
import time
started_at = time.time()
result = sum(range(10_000))
finished_at = time.time()
print(started_at, finished_at)
Those values are useful wall-clock timestamps. Their difference can be distorted by clock corrections, so prefer perf_counter() for a duration, even when a simple subtraction appears to work.
Benchmark a small operation with timeit
import timeit
repeats = timeit.repeat("sum(range(1000))", number=10_000, repeat=5)
best_per_call = min(repeats) / 10_000
print(f"Best observed time per call: {best_per_call * 1000:.6f} ms")
Each result is the total for 10,000 executions. Repetition helps reveal variability; it does not remove all noise or make a microbenchmark representative of production. Keep setup, input sizes, and interpreter versions comparable. timeit disables garbage collection by default during timing, which can matter for allocation-heavy workloads.
Reuse a timer around a larger operation
from contextlib import contextmanager
import time
@contextmanager
def timer(name):
start = time.perf_counter()
try:
yield
finally:
print(f"{name}: {time.perf_counter() - start:.6f}s")
with timer("Processing"):
result = sum(range(1_000_000))
The finally block reports elapsed time even when the measured code raises; it does not suppress that exception. A similar wrapper can be implemented as a decorator when you need function-level measurements.
FAQ
How do I measure async work? Start the timer before the await and stop it afterward. That measures elapsed time, including waits. Merely creating a coroutine object does not execute it.
What about CPU time? time.process_time() measures the process's CPU time and excludes sleep. Choose it when that is the metric you need.
How do I find the slow function or line? cProfile attributes time to function calls. A tool such as line_profiler provides line-level profiling; cProfile itself is not a per-line timer.
References
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan