Performance Highlights Summary
Every highlight bullet from the 12 pages in this section, gathered on one page and grouped by the page it came from.
- Python's overhead comes from the eval loop, boxed objects, and the GIL - three separate costs with three separate fixes
- A fast timeit result and a fast cProfile result answer different questions - benchmarking is not profiling
- Vectorization, Cython, and PyPy all remove interpreter overhead, but at different scopes: one call, one function, or the whole program
- The GIL blocks CPU parallelism across threads but not I/O overlap - conflating the two leads to the wrong fix
- Caching trades memory for repeated computation and is orthogonal to every other lever on this page
- Measure baseline performance before any optimization attempt
- Use timeit to isolate statement overhead for microbenchmarks
- Check cProfile cumulative time to identify top optimization targets
- Compare tracemalloc snapshots before and after to find memory leaks
- Profile comprehensions against loops before assuming speed gains
- Use generators for file I/O and pipeline processing to save memory
- timeit disables garbage collection and setup overhead to ensure fair comparisons
- Increase iterations until total runtime exceeds 0.2 seconds for reliable results
- Always report median time from multiple runs instead of the best single time
- Benchmark your code at realistic input sizes or miss O(n) vs O(n^2) differences
- Profile the full application first before optimizing based on microbenchmarks
- Store or use results in loops to prevent compiler dead code elimination
- Use py-spy for production (low overhead), cProfile for dev deep dives
- Sort by cumtime in cProfile results to find actual hotspots, not tottime
- Profiling toy data gives inaccurate hotspots; use production-like data sizes
- py-spy needs no code changes and profiles live processes safely
- Flame graphs show hot paths as stack-width proportional to time spent
- cProfile overhead reaches 30% (dev only), py-spy stays under 5%
- Start with tracemalloc for low-overhead line-level profiling in staging
- Call gc.collect before each snapshot or profiling results hide leaks
- CPython keeps memory pools, measure with psutil RSS not Python objects
- Pandas and Polars transforms copy data, use in-place ops or lazy eval
- Memray tracks every allocation with native speed for detailed analysis
- Compare snapshots over time to prove memory growth is real before fixing
- NumPy vectorization yields 10-100x speedups by running ops in compiled C code
- NumPy works best for arrays >1000 elements; use loops for small data
- Create arrays once and stay in NumPy to avoid repeated conversion overhead
- Broadcasting lets different-shaped arrays operate together without tiling
- Use numeric dtypes, not object, to access compiled C performance gains
- Use out= parameter to avoid memory intermediates in chained operations
- Don't compile I/O-bound code or already-vectorized NumPy libraries
- Profile hotspots first before adding Cython compilation complexity
- Typed memoryviews and cdef declarations unlock 10-100x speedup
- Cython for numeric kernels with C compiler, mypyc for typed Python
- mypyc dynamic code falls back to Python, requiring strict typing
- Build Cython wheels with cibuildwheel for platform distribution
- PyPy JIT compiles hot loops 20x faster than CPython for pure-Python workloads
- PyPy requires JIT warmup in long-running processes before peak performance
- Short-lived CLI processes never complete PyPy JIT warmup, use servers instead
- C extensions may negate PyPy gains, benchmark the full app before migrating
- Free-threaded Python 3.14 removes GIL for true thread parallelism
- Only deploy PyPy if benchmarks show clear wins on your specific workload
- Use lru_cache for single-process pure functions called repeatedly
- Cache expensive database and HTTP operations to reduce latency
- Always set maxsize on lru_cache to prevent memory leaks
- Return copies from cached functions to prevent caller mutations
- Use Redis or Memcached for caches shared across app instances
- Monitor cache_info() hit rate and tune maxsize for performance
- Pick asyncio for I/O, multiprocessing for CPU, threading for blocking libraries
- Limit concurrent requests with asyncio.Semaphore to prevent server overload
- Use asyncio.to_thread to run blocking calls without freezing the event loop
- ProcessPoolExecutor bypasses the GIL for CPU-bound parallelism
- Set return_exceptions=True in asyncio.gather to prevent cascading failures
- Top-down audit workflow: requirements, measurement, profiling, fix, verify
- Define SLA upfront with p95 latency or throughput before measuring baselines
- Establish production-like baseline with representative workload and data
- Profile CPU with cProfile or py-spy, identify top 3 hotspots by cumulative time
- Batch or join ORM queries to eliminate N+1 database patterns
- Stop optimizing when SLA is met, further work adds complexity without value
- Profile with cProfile or py-spy before optimizing, never guess
- Right algorithm first beats micro-optimized code
- Use asyncio for I/O-bound work, multiprocessing for CPU-bound
- Add maxsize and TTL to every cache for safety
- Stop optimizing once SLA targets are met, diminishing returns follow
- Database tuning through indexes often beats application-level optimization
Revisado por Chris St. John·Última actualización: 31 jul 2026