Skip to main content

cProfile, pstats, reading hotspots

advanced20 min readLesson 131 of 169

Find where time actually goes; extract hotspot data programmatically.

The profiling toolkit: cProfile and pstats

cProfile records per-function call counts and cumulative time:

import cProfile, pstats, io

def workload():
    ...  # the slow thing

pr = cProfile.Profile()
pr.enable()
workload()
pr.disable()

stats = pstats.Stats(pr)
stats.sort_stats("tottime")       # self time per function
stats.print_stats(5)              # top 5 offenders

Reading a profile like a professional:

  • tottime (self time) โ€” time in the function's own body, excluding callees. Sort by this to find the actual hotspot.
  • cumtime (cumulative) โ€” including everything it calls. Sort by this to find expensive subtrees.
  • ncalls โ€” call counts. A function called 500k times at 1ยตs each is a hotspot; a function called once at 500ms is a different kind of problem.
  • stats.print_callers() shows who calls the hotspot โ€” the refactoring target is often the caller, not the callee.

You can extract rows programmatically: pstats.Stats(pr).stats maps (filename, lineno, funcname) โ†’ (cc, nc, tt, ct, callers). That is exactly what this module's challenges use to grade "find the bottleneck" โ€” no guessing.

Now practice

Profiling PracticeFind the bottleneck programmatically: sort by self time, ignore the wrapper.1 challenge ยท ยท ~20 min