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.
Python code
32 linesimport 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
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
- Call `stats.print_stats(10)` to show only the top 10 lines.
- 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
More from Concurrency & performance
- Benchmark list.append vs deque.append in Python medium
- Build a Python Performance Profiler That Generates Readable Reports medium
- Graceful Shutdown Executor Context Manager in Python medium
- How to Build a Producer-Consumer Pattern with asyncio.Queue in Python medium
- How to Cancel an asyncio Task with Graceful Cleanup in Python medium
- How to Convert Data in Parallel with ThreadPoolExecutor in Python easy
Keep learning
Related tutorials and quizzes for this topic.