Profile Python functions with cProfile

Profile a Python program with cProfile, capture the stats in memory, and print a sorted performance report.

Easy Python 3.9+ Aug 9, 2026 Functions & basics 11 views 0 copies

Python code

38 lines
Python 3.9+
import cProfile
import pstats
import io


def slow_function():
    total = 0
    for i in range(100000):
        total += i ** 2
    return total


def medium_function():
    return sum(range(10000))


def fast_function():
    return sum(range(100))


def main():
    result1 = slow_function()
    result2 = medium_function()
    result3 = fast_function()
    print(f"Results: {result1}, {result2}, {result3}")


if __name__ == "__main__":
    profiler = cProfile.Profile()
    profiler.enable()
    main()
    profiler.disable()

    stream = io.StringIO()
    stats = pstats.Stats(profiler, stream=stream)
    stats.sort_stats("cumulative")
    stats.print_stats()
    print(stream.getvalue())

Output

stdout
Results: 333328333350000, 49995000, 4950
         1000004 function calls in 0.097 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.097    0.097 {built-in method builtins.exec}
        1    0.000    0.000    0.097    0.097 <string>:1(<module>)
        1    0.000    0.000    0.097    0.097 /tmp/example.py:16(main)
        1    0.040    0.040    0.082    0.082 /tmp/example.py:5(slow_function)
   100000    0.042    0.000    0.042    0.000 {built-in method builtins.sum}

How it works

cProfile.Profile() creates a fresh profiler object that records every function call. Calling enable() starts recording and disable() stops it, wrapping only the main() call so external imports aren't counted. pstats.Stats reads the raw profile data and you can direct its text output into any file-like object (here an io.StringIO) instead of the default stdout. sort_stats('cumulative') orders functions by total time spent in them, making it easy to spot the slowest path. The printed report shows ncalls, tottime, and cumtime so you can separate each call's own cost from the cost of everything it calls.

Common mistakes

  • Forgetting that `enable()`/`disable()` wrap the work — profile too much or too little code by misplacing them
  • Printing `stats.print_stats()` without redirecting to a stream, which clutters the console output
  • Not importing `io` before using `StringIO` as the stats destination

Variations

  1. Use `python -m cProfile script.py` from the command line for a quick run without modifying source
  2. Save stats to a file with `stats.dump_stats('out.prof')` and analyze later with `pstats` or SnakeViz

Real-world use cases

  • Finding the bottleneck function in a batch job before optimizing the hot path.
  • Measuring the cost of an expensive API call in a worker before adding caching.
  • Comparing candidate implementations during a performance refactor to keep the fastest one.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Functions & basics

Related tutorials and quizzes for this topic.