Performance Optimization in Python
Optimize by measuring first, choosing the right algorithms/data structures, and leveraging libraries.
Profile first
Identify hotspots before changing code.
python -m cProfile -o prof.out app.py
snakeviz prof.out
Line and memory profilers: line_profiler, memory_profiler.
Algorithmic improvements
Big-O dominates. Replace nested loops with maps/sets, early exits, and better structures.
# O(n) membership
s = set(items)
if x in s: ...
Data structures
Pick the right container for the job:
listfor ordered, append-heavy sequencesdequefor queue/stack endsset/dictfor O(1) membership/lookuparray/bytearrayfor compact numeric/byte data
Vectorization and C extensions
Use NumPy/Pandas for vectorized numeric/data ops; offload hot loops to C/Cython/Numba.
import numpy as np
x = np.arange(1_000_000)
y = x * 2
I/O and concurrency
Batch I/O, stream with iterators, and parallelize appropriately:
- Async IO for many network operations
concurrent.futuresfor CPU bound (processes) or blocking I/O (threads)
Caching and memoization
Memoize pure functions; be mindful of memory keys.
from functools import lru_cache
@lru_cache(maxsize=1024)
def fib(n): ...
Micro-optimizations (sparingly)
- Avoid repeated attribute lookups in tight loops (bind to locals)
- Use comprehensions/generator expressions
Best practices
- Write clear code first; optimize where profiling shows value
- Add benchmarks/tests to prevent performance regressions
- Consider alternative interpreters (PyPy) for long-running workloads
Summary
- Measure, then optimize with algorithms/structures, vectorization, and appropriate concurrency
- Cache wisely; keep code clear and validated with benchmarks