The Py Hub

Finding slow Python code: timeit and cProfile

Guides3 min read20 October 2026

By · Published

How to find out why Python code is slow before trying to optimise it: timing snippets with timeit and perf_counter, profiling whole programs with cProfile and pstats, reading the output, visualising results, and common fixes.

The first rule of speeding up code is to measure before you change anything. Guessing where the slow part is usually leads to optimising the wrong thing. Python's standard library includes the tools you need.

Quick timing with perf_counter

from time import perf_counter
start = perf_counter()
result = do_work()
print(f"took {perf_counter() - start:.3f}s")

perf_counter is a high-resolution clock suited to measuring durations. Use it for a rough idea of how long a step takes.

Comparing small snippets with timeit

timeit runs code many times and reports the best result, reducing noise from other activity on your computer.

python -m timeit "sum(range(1000))"
python -m timeit -s "data = list(range(1000))" "[x*2 for x in data]"

The -s option runs setup code once, so it isn't included in the timing. From Python code:

import timeit
print(min(timeit.repeat("'-'.join(map(str, range(100)))", number=10_000, repeat=5)))

In Jupyter or IPython, the %timeit magic does the same.

Profiling a whole program with cProfile

When you don't know which part is slow, profile:

python -m cProfile -s cumulative my_script.py

Key columns in the output:

Sort by cumulative to find the expensive high-level steps, then by tottime to find the specific functions doing the work.

Saving and exploring results

python -m cProfile -o profile.out my_script.py
python -c "import pstats; pstats.Stats('profile.out').sort_stats('tottime').print_stats(15)"

Third-party viewers such as SnakeViz show the same data as an interactive chart. For line-by-line detail, line_profiler can time each line of a chosen function. Sampling profilers such as py-spy can attach to a running program with little overhead.

Common causes of slow Python, and fixes

A worked example

Suppose a script that matches customer emails against a list of 50,000 unsubscribed addresses takes minutes. Profiling shows almost all the tottime inside the loop that checks if email in unsubscribed, where unsubscribed is a list. Each check scans the whole list. Converting it once with unsubscribed = set(unsubscribed) makes each lookup take roughly constant time, and the script finishes in seconds. Re-running the profile confirms the hotspot has gone, and the tests confirm the output hasn't changed. That's the pattern: measure, fix the biggest hotspot, measure again.

Good habits

Frequently asked questions

Does profiling slow my program down?

cProfile adds overhead, so absolute times are inflated, but the relative picture is still useful.

Will a newer Python version be faster?

Often. Recent releases have included significant speed improvements, so upgrading to a supported version is worth testing.

Sources and further reading

Facts checked on 20 October 2026. Spotted an error? Tell us and we will correct it.

Get the next guide first

New articles land every week. Follow along by RSS or bookmark the homepage.

Follow by RSS