profile
The Python profile module is a pure Python deterministic profiler that measures how often and how long each part of a program runs. It exposes the same interface as cProfile and produces profiling data that can be formatted into reports through the pstats module:
>>> import profile
>>> profile.run("sum(i * i for i in range(1000))")
1006 function calls in 0.032 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.002 0.002 0.004 0.004 :0(sum)
1001 0.002 0.000 0.002 0.000 <string>:1(<genexpr>)
1 0.000 0.000 0.004 0.004 <string>:1(<module>)
Each row of the report describes one function in the profiled code:
ncalls: number of calls to that functiontottime: total time spent inside the function, excluding sub-callspercall: time per call based ontottimecumtime: cumulative time including everything the function calledpercall: time per call based oncumtimefilename:lineno(function): location of the profiled code
Because profile is written in Python, it adds more overhead than its C counterpart cProfile, which is usually preferred for long-running programs. The profile module remains useful when you need a profiler that is easy to read, extend, or subclass in pure Python.
Key Features
- Records every function call, return, and exception during program execution
- Collects call counts, total time, and cumulative time per function
- Profiles an expression, a callable, or an entire script or module
- Writes results to a file that
pstatscan later load and sort - Supports a custom timer function for specialized measurements
- Provides a calibration hook to subtract profiler overhead from timings
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
profile.run() |
Function | Profiles an expression string and prints or saves the results |
profile.runctx() |
Function | Profiles an expression with explicit globals and locals |
profile.Profile |
Class | Represents a single profiling session that can be driven directly |
profile.Profile.runcall() |
Method | Profiles a single callable with given arguments |
profile.Profile.dump_stats() |
Method | Saves collected statistics to a binary file for later analysis |
pstats.Stats |
Class | Loads profiling data and formats it into sortable reports |
Examples
Profiling a small expression and printing the report:
>>> import profile
>>> profile.run("[i ** 2 for i in range(100)]")
4 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:1(<listcomp>)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
Profiling a callable directly with the Profile.runcall() method:
>>> import profile
>>> def work(n):
... return sum(i * i for i in range(n))
...
>>> pr = profile.Profile()
>>> pr.runcall(work, 1000)
332833500
>>> pr.print_stats()
1005 function calls in 0.005 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.003 0.003 <stdin>:1(work)
1001 0.001 0.000 0.001 0.000 <stdin>:2(<genexpr>)
1 0.001 0.001 0.003 0.003 :0(sum)
Saving results to a file and inspecting them with pstats:
>>> import profile, pstats
>>> profile.run("sum(range(100_000))", "out.prof")
>>> stats = pstats.Stats("out.prof")
>>> stats.strip_dirs().sort_stats("cumulative").print_stats(5)
Tue May 19 15:42:16 2026 out.prof
5 function calls in 0.002 seconds
Ordered by: cumulative time
List reduced from 6 to 5 due to restriction <5>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.002 0.002 profile:0(sum(range(100_000)))
1 0.000 0.000 0.002 0.002 :0(exec)
1 0.000 0.000 0.002 0.002 <string>:1(<module>)
1 0.002 0.002 0.002 0.002 :0(sum)
1 0.000 0.000 0.000 0.000 :0(setprofile)
Profiling a whole script from the command line:
$ python -m profile -o out.prof myscript.py
Common Use Cases
The most common tasks for profile include:
- Finding slow functions in a script or module
- Capturing profiling data for later analysis with
pstats - Subclassing the profiler to collect custom metrics
- Measuring execution time with a non-default timer function
- Comparing the cost of alternative implementations of a routine
Real-World Example
Consider a function that computes prime numbers using a naive algorithm. You can wrap the call in a Profile instance, dump the results to a file, and use pstats to print the five most expensive functions sorted by cumulative time:
profile_primes.py
import profile
import pstats
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
def count_primes(limit):
return sum(1 for n in range(limit) if is_prime(n))
pr = profile.Profile()
pr.runcall(count_primes, 20_000)
pr.dump_stats("primes.prof")
stats = pstats.Stats("primes.prof")
stats.strip_dirs().sort_stats("cumulative").print_stats(5)
Running the script prints a table that shows how much time was spent inside count_primes() and is_prime(), which helps locate the bottleneck before refactoring or switching to a faster algorithm.
Related Resources
Tutorial
Profiling in Python: How to Find Performance Bottlenecks
In this tutorial, you'll learn how to profile your Python programs using numerous tools available in the standard library, third-party libraries, as well as a powerful tool foreign to Python. Along the way, you'll learn what profiling is and cover a few related concepts.
For additional information on related topics, take a look at the following resources:
- Python 3.12 Preview: Support For the Linux perf Profiler (Tutorial)
- Speed Up Your Python Program With Concurrency (Tutorial)
- Caching in Python Using the LRU Cache Strategy (Tutorial)
- Profiling Performance in Python (Course)
- Speed Up Python With Concurrency (Course)
- Python Concurrency (Quiz)
- Caching in Python With lru_cache (Course)
By Leodanis Pozo Ramos • Updated May 19, 2026