Python Performance Optimization
Profile, analyze, and optimize Python code for better performance - CPU profiling, memory optimization, and implementation best practices.
When to Invoke
- User reports slow Python code or asks to speed up execution
- Profiling or benchmarking Python applications
- Reducing CPU time, memory consumption, or I/O wait
- Optimizing database queries or data processing pipelines
- Debugging memory leaks or excessive memory usage
- Choosing between parallelization strategies (threading, multiprocessing, async)
- Evaluating algorithmic vs implementation-level improvements
Core Concepts
Profiling Types
- CPU Profiling: Identify time-consuming functions (cProfile, py-spy)
- Memory Profiling: Track memory allocation and leaks (tracemalloc, memory_profiler)
- Line Profiling: Profile at line-by-line granularity (line_profiler)
- Call Graph: Visualize function call relationships
Performance Metrics
- Execution Time: How long operations take
- Memory Usage: Peak and average memory consumption
- CPU Utilization: Processor usage patterns
- I/O Wait: Time spent on I/O operations
Optimization Strategies
- Algorithmic: Better algorithms and data structures
- Implementation: More efficient code patterns
- Parallelization: Multi-threading/processing
- Caching: Avoid redundant computation
- Native Extensions: C/Rust for critical paths
Quick Start
import time
import timeit
# Simple timing
start = time.time()
result = sum(range(1000000))
print(f"Execution time: {time.time() - start:.4f} seconds")
# Accurate benchmarking with timeit
execution_time = timeit.timeit("sum(range(1000000))", number=100)
print(f"Average time: {execution_time/100:.6f} seconds")
Profiling Tools Summary
cProfile - CPU Profiling
python -m cProfile -o output.prof script.py
python -m pstats output.prof
line_profiler - Line-by-Line
uv add --dev line-profiler # or: uv tool install line-profiler
kernprof -l -v script.py
memory_profiler - Memory Usage
uv add --dev memory-profiler
python -m memory_profiler script.py
py-spy - Production Profiling
uv tool install py-spy
py-spy record -o profile.svg -- python script.py
py-spy top --pid 12345
Key Optimization Patterns
Data Structure Selection
- Dict/Set for lookups: O(1) vs O(n) for list search
- Generators for large datasets: Constant memory vs full list
- slots on classes: Reduces per-instance memory
Code-Level Optimizations
- List comprehensions over loops (faster C implementation)
str.join() over += concatenation
- Local variables over global access in hot loops
- Inline simple operations in tight loops
- Built-in functions (implemented in C)
Caching
functools.lru_cache for expensive pure functions
weakref.WeakValueDictionary for GC-friendly caches
Parallelization
- multiprocessing: CPU-bound tasks, true parallelism
- threading: I/O-bound tasks with shared memory
- asyncio: I/O-bound tasks with many concurrent operations
Memory Optimization
tracemalloc for detecting memory leaks (snapshot comparison)
- Iterators over lists for file/stream processing
weakref caches to allow garbage collection
Database Optimization
- Batch operations with
executemany() and single commit
- Index frequently queried columns
- Select only needed columns (avoid
SELECT *)
- Use
EXPLAIN QUERY PLAN for analysis
Benchmarking
from functools import wraps
import time
def benchmark(func):
"""Decorator to benchmark function execution."""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.6f} seconds")
return result
return wrapper
For pytest-based benchmarking: pip install pytest-benchmark
Best Practices
- Profile before optimizing - measure to find real bottlenecks
- Focus on hot paths - optimize code that runs most frequently
- Use appropriate data structures - dict for lookups, set for membership
- Avoid premature optimization - clarity first, then optimize
- Use built-in functions - they are implemented in C
- Cache expensive computations - use lru_cache
- Batch I/O operations - reduce system calls
- Use generators for large datasets
- Consider NumPy for numerical operations
- Profile production code - use py-spy for live systems
Common Pitfalls
- Optimizing without profiling
- Using global variables unnecessarily
- Not using appropriate data structures
- Creating unnecessary copies of data
- Not using connection pooling for databases
- Ignoring algorithmic complexity
- Over-optimizing rare code paths
- Not considering memory usage
Performance Checklist
References
references/optimization-patterns.md - detailed code examples for all profiling tools, optimization patterns (list comprehensions, generators, string concat, dict lookups, local vars, function call overhead), advanced optimization (NumPy, lru_cache, slots, multiprocessing, async I/O), database optimization, memory leak detection, and benchmarking tools
Resources
- cProfile: Built-in CPU profiler
- memory_profiler: Memory usage profiling
- line_profiler: Line-by-line profiling
- py-spy: Sampling profiler for production
- NumPy: High-performance numerical computing
- Cython: Compile Python to C
- PyPy: Alternative Python interpreter with JIT
1---2name: python-performance-optimization3description: Measure first with cProfile, line_profiler, memory_profiler or py-spy, then fix what the numbers show. TRIGGER WHEN: debugging slow Python code, optimizing bottlenecks, cutting memory usage, or improving application performance.4---56# Python Performance Optimization78Profile, analyze, and optimize Python code for better performance - CPU profiling, memory optimization, and implementation best practices.910## When to Invoke1112- User reports slow Python code or asks to speed up execution13- Profiling or benchmarking Python applications14- Reducing CPU time, memory consumption, or I/O wait15- Optimizing database queries or data processing pipelines16- Debugging memory leaks or excessive memory usage17- Choosing between parallelization strategies (threading, multiprocessing, async)18- Evaluating algorithmic vs implementation-level improvements1920## Core Concepts2122### Profiling Types23- **CPU Profiling**: Identify time-consuming functions (cProfile, py-spy)24- **Memory Profiling**: Track memory allocation and leaks (tracemalloc, memory_profiler)25- **Line Profiling**: Profile at line-by-line granularity (line_profiler)26- **Call Graph**: Visualize function call relationships2728### Performance Metrics29- **Execution Time**: How long operations take30- **Memory Usage**: Peak and average memory consumption31- **CPU Utilization**: Processor usage patterns32- **I/O Wait**: Time spent on I/O operations3334### Optimization Strategies35- **Algorithmic**: Better algorithms and data structures36- **Implementation**: More efficient code patterns37- **Parallelization**: Multi-threading/processing38- **Caching**: Avoid redundant computation39- **Native Extensions**: C/Rust for critical paths4041## Quick Start4243```python44import time45import timeit4647# Simple timing48start = time.time()49result = sum(range(1000000))50print(f"Execution time: {time.time() - start:.4f} seconds")5152# Accurate benchmarking with timeit53execution_time = timeit.timeit("sum(range(1000000))", number=100)54print(f"Average time: {execution_time/100:.6f} seconds")55```5657## Profiling Tools Summary5859### cProfile - CPU Profiling60```bash61python -m cProfile -o output.prof script.py62python -m pstats output.prof63```6465### line_profiler - Line-by-Line66```bash67uv add --dev line-profiler # or: uv tool install line-profiler68kernprof -l -v script.py69```7071### memory_profiler - Memory Usage72```bash73uv add --dev memory-profiler74python -m memory_profiler script.py75```7677### py-spy - Production Profiling78```bash79uv tool install py-spy80py-spy record -o profile.svg -- python script.py81py-spy top --pid 1234582```8384## Key Optimization Patterns8586### Data Structure Selection87- **Dict/Set for lookups**: O(1) vs O(n) for list search88- **Generators for large datasets**: Constant memory vs full list89- **__slots__ on classes**: Reduces per-instance memory9091### Code-Level Optimizations92- List comprehensions over loops (faster C implementation)93- `str.join()` over `+=` concatenation94- Local variables over global access in hot loops95- Inline simple operations in tight loops96- Built-in functions (implemented in C)9798### Caching99- `functools.lru_cache` for expensive pure functions100- `weakref.WeakValueDictionary` for GC-friendly caches101102### Parallelization103- **multiprocessing**: CPU-bound tasks, true parallelism104- **threading**: I/O-bound tasks with shared memory105- **asyncio**: I/O-bound tasks with many concurrent operations106107### Memory Optimization108- `tracemalloc` for detecting memory leaks (snapshot comparison)109- Iterators over lists for file/stream processing110- `weakref` caches to allow garbage collection111112### Database Optimization113- Batch operations with `executemany()` and single commit114- Index frequently queried columns115- Select only needed columns (avoid `SELECT *`)116- Use `EXPLAIN QUERY PLAN` for analysis117118## Benchmarking119120```python121from functools import wraps122import time123124def benchmark(func):125 """Decorator to benchmark function execution."""126 @wraps(func)127 def wrapper(*args, **kwargs):128 start = time.perf_counter()129 result = func(*args, **kwargs)130 elapsed = time.perf_counter() - start131 print(f"{func.__name__} took {elapsed:.6f} seconds")132 return result133 return wrapper134```135136For pytest-based benchmarking: `pip install pytest-benchmark`137138## Best Practices1391401. **Profile before optimizing** - measure to find real bottlenecks1412. **Focus on hot paths** - optimize code that runs most frequently1423. **Use appropriate data structures** - dict for lookups, set for membership1434. **Avoid premature optimization** - clarity first, then optimize1445. **Use built-in functions** - they are implemented in C1456. **Cache expensive computations** - use lru_cache1467. **Batch I/O operations** - reduce system calls1478. **Use generators** for large datasets1489. **Consider NumPy** for numerical operations14910. **Profile production code** - use py-spy for live systems150151## Common Pitfalls152153- Optimizing without profiling154- Using global variables unnecessarily155- Not using appropriate data structures156- Creating unnecessary copies of data157- Not using connection pooling for databases158- Ignoring algorithmic complexity159- Over-optimizing rare code paths160- Not considering memory usage161162## Performance Checklist163164- [ ] Profiled code to identify bottlenecks165- [ ] Used appropriate data structures166- [ ] Implemented caching where beneficial167- [ ] Optimized database queries168- [ ] Used generators for large datasets169- [ ] Considered multiprocessing for CPU-bound tasks170- [ ] Used async I/O for I/O-bound tasks171- [ ] Minimized function call overhead in hot loops172- [ ] Checked for memory leaks173- [ ] Benchmarked before and after optimization174175## References176177- `references/optimization-patterns.md` - detailed code examples for all profiling tools, optimization patterns (list comprehensions, generators, string concat, dict lookups, local vars, function call overhead), advanced optimization (NumPy, lru_cache, __slots__, multiprocessing, async I/O), database optimization, memory leak detection, and benchmarking tools178179## Resources180181- **cProfile**: Built-in CPU profiler182- **memory_profiler**: Memory usage profiling183- **line_profiler**: Line-by-line profiling184- **py-spy**: Sampling profiler for production185- **NumPy**: High-performance numerical computing186- **Cython**: Compile Python to C187- **PyPy**: Alternative Python interpreter with JIT