cProfile Module¶
Python's built-in cProfile module provides a deterministic profiler that measures CPU time spent in each function. It's essential for identifying performance bottlenecks in your code.
Basic Usage¶
The cProfile module offers several ways to profile code: command-line usage, direct module calls, or context managers.
Command-line Profiling¶
# example_module.py
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
def main():
result = fibonacci(30)
print(f"Result: {result}")
if __name__ == "__main__":
main()
Run with profiling:
python -m cProfile -s cumulative example_module.py
Programmatic Profiling¶
import cProfile
import pstats
from io import StringIO
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# Create profiler and run code
profiler = cProfile.Profile()
profiler.enable()
result = fibonacci(30)
profiler.disable()
# Print statistics
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(10) # Top 10 functions
Interpreting Results¶
cProfile output shows: - ncalls: Number of calls to the function - tottime: Total time spent in the function (excluding subfunctions) - cumtime: Cumulative time (including called functions) - filename:lineno(function): Where the function is defined
ncalls tottime cumtime filename:lineno(function)
832039 0.156 0.321 example.py:1(fibonacci)
1 0.000 0.320 example.py:6(main)
Saving and Loading Profiling Data¶
import cProfile
import pstats
profiler = cProfile.Profile()
profiler.enable()
# Your code here
for i in range(1000):
sum([i**2 for i in range(100)])
profiler.disable()
# Save to file
profiler.dump_stats('profile_data.prof')
# Load and analyze later
stats = pstats.Stats('profile_data.prof')
stats.sort_stats('cumulative')
stats.print_stats(5)
Performance Tips¶
- Use
sort_stats()with 'cumulative' for wall-clock time analysis - Filter results with
print_stats(10)to see top functions - Compare profiles before and after optimizations
- Profile on realistic data to get accurate measurements
Runnable Example: cprofile_tutorial.py¶
"""
PYTHON CODE PROFILING & OPTIMIZATION - INTERMEDIATE LEVEL
==========================================================
Module 5: cProfile Basics - Function-Level Profiling
LEARNING OBJECTIVES:
- Master cProfile for function-level profiling
- Understand profiling output metrics
- Learn to identify performance bottlenecks
- Profile scripts and functions
- Interpret profiling data effectively
Author: Python Course Development Team
Date: 2024
"""
import cProfile
import pstats
from io import StringIO
# =============================================================================
# Definitions
# =============================================================================
def fibonacci_recursive(n):
"""Inefficient recursive Fibonacci"""
if n <= 1:
return n
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)
def fibonacci_iterative(n):
"""Efficient iterative Fibonacci"""
if n <= 1:
return n
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
def demonstrate_basic_profiling():
"""Basic cProfile usage"""
print("=" * 70)
print("BASIC CPROFILE USAGE")
print("=" * 70)
print("\nProfiling recursive Fibonacci(20):\n")
# Profile using cProfile.run()
cProfile.run('fibonacci_recursive(20)')
def profile_with_stats():
"""Profile and analyze with pstats"""
print("\n" + "=" * 70)
print("PROFILING WITH PSTATS")
print("=" * 70)
# Create profiler
profiler = cProfile.Profile()
# Profile the code
profiler.enable()
result = fibonacci_recursive(25)
profiler.disable()
# Analyze results
stats = pstats.Stats(profiler)
stats.strip_dirs()
stats.sort_stats('cumulative')
print(f"\nFibonacci(25) = {result}\n")
print("Top 10 functions by cumulative time:")
stats.print_stats(10)
def compare_implementations():
"""Compare recursive vs iterative"""
print("\n" + "=" * 70)
print("COMPARING IMPLEMENTATIONS")
print("=" * 70)
n = 30
print(f"\n1. Recursive Fibonacci({n}):")
profiler1 = cProfile.Profile()
profiler1.enable()
result1 = fibonacci_recursive(n)
profiler1.disable()
stats1 = pstats.Stats(profiler1)
print(f" Result: {result1}")
print(f" Total calls: {stats1.total_calls:,}")
print(f"\n2. Iterative Fibonacci({n}):")
profiler2 = cProfile.Profile()
profiler2.enable()
result2 = fibonacci_iterative(n)
profiler2.disable()
stats2 = pstats.Stats(profiler2)
print(f" Result: {result2}")
print(f" Total calls: {stats2.total_calls:,}")
print(f"\nRecursive made {stats1.total_calls:,} function calls!")
print(f"Iterative made {stats2.total_calls:,} function calls!")
def main():
"""Run all demonstrations"""
print("\n" + "=" * 70)
print("CPROFILE BASICS - COMPREHENSIVE TUTORIAL")
print("=" * 70 + "\n")
demonstrate_basic_profiling()
profile_with_stats()
compare_implementations()
print("\n" + "=" * 70)
print("KEY METRICS IN CPROFILE OUTPUT")
print("=" * 70)
print("""
ncalls: Number of calls to the function
tottime: Total time spent in function (excluding subcalls)
percall: tottime / ncalls
cumtime: Cumulative time (including subcalls)
percall: cumtime / ncalls
filename:lineno(function)
Key Insights:
- High cumtime: Function takes significant total time
- High tottime: Function itself is slow (not subcalls)
- High ncalls: Function is called very frequently
- Focus optimization on high cumtime AND high tottime
""")
# =============================================================================
# Main
# =============================================================================
if __name__ == "__main__":
main()