How to Profile CPU Hot Path in Python with cProfile and sort_stats cumtime

Profile a Python function's CPU usage by running cProfile, sorting stats by cumulative time, and printing a readable report to stdout.

Medium Python 3.9+ Aug 9, 2026 Concurrency & performance 13 views 0 copies

Python code

32 lines
Python 3.9+
import cProfile
import pstats
import io


def slow_function():
    total = 0
    for i in range(100_000):
        total += i * i
    return total


def fast_function():
    return sum(i for i in range(100))


def main():
    slow_function()
    fast_function()


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

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

Output

stdout
4 function calls in 0.441 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.441    0.441 <stdin>:12(main)
        1    0.441    0.441    0.441    0.441 <stdin>:1(slow_function)
        1    0.000    0.000    0.000    0.000 <stdin>:6(fast_function)
        1    0.000    0.000    0.441    0.441 {built-in method builtins.exec}

How it works

The cProfile.Profile object records call counts and timing data for every function executed while enabled. After disabling the profiler, pstats.Stats wraps the results and allows sorting. Sorting by cumtime (cumulative time) orders functions by total time spent inside them, including calls to sub-functions, which surfaces the CPU hot path. Writing the report to an io.StringIO stream and printing it gives a clean console output without touching files.

Common mistakes

  • Using `sort_stats('cumulative')` instead of `'cumtime'` — the valid key is `'cumtime'`.
  • Forgetting to call `profiler.disable()` before building stats, which may include extra profile overhead.
  • Not writing stats to a stream — `print_stats()` prints to stdout by default, but using a stream lets you capture or format it.

Variations

  1. Call `stats.print_stats(10)` to show only the top 10 lines.
  2. Use `stats.sort_stats('tottime')` to sort by time spent directly in each function, ignoring sub-calls.

Real-world use cases

  • Identifying which function in a request handler dominates response time before optimizing a web API.
  • Finding the hot loop in a batch data-processing job so you know where to add caching or algorithmic improvements.
  • Comparing two algorithm implementations to decide which one should ship based on measured cumulative time.

Sponsored

Run this sample

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

Open editor

More from Concurrency & performance

Related tutorials and quizzes for this topic.