authx-extra package includes a profiling middleware powered by pyinstrument, a statistical Python profiler that helps you identify performance bottlenecks in your FastAPI application.
Introduction to pyinstrument
Pyinstrument is a statistical Python profiler which records the call stack every 1ms rather than recording the whole trace. This approach is designed to avoid profiling overhead which can increase significantly if some functions are called many times while not taking much time to complete.Benefits of statistical profiling
- Low overhead: Avoids profiling noise by removing profiling information of faster parts of code
- Accurate results: Highlights code that is actually slow, not just frequently called
- Focused insights: Shows the parts of your code that matter most for performance
Trade-offs
Some function calls that run very quickly may not be recorded, but since they’re already fast, this typically doesn’t affect optimization efforts.Pyinstrument uses OS signals to ask the OS to send a signal and handle signals using a Python signal handler (
PyEval_SetProfile) for recording every 1ms.Add profiling to your application
Integrating profiling into your FastAPI application is straightforward:Middleware configuration
TheProfilerMiddleware accepts several configuration options:
Parameters
- profiler_output_type: Output format for profiling data (
"text","html", or"json") - is_print_each_request: Whether to print profiling information for each request (default:
False) - html_file_name: Name of the HTML file to save profiling results (default:
"profiling.html")
Output formats
The profiler supports three output formats:Text output
Plain text format showing the call stack and timing information:HTML output
An interactive HTML report with collapsible call stacks and visual timing information. Perfect for detailed analysis.JSON output
Machine-readable JSON format for programmatic analysis or integration with other tools:Example profiling scenario
Consider this simple example:random.randint() rather than the add() function, even though add() is called just as frequently.
Per-request profiling
Enableis_print_each_request to see profiling output for every request:
Best practices
Development environment
Use profiling during development to:- Identify slow endpoints
- Optimize database queries
- Find inefficient algorithms
- Validate performance improvements
Production environment
For production, consider using profiling selectively:
- Only enable profiling for specific endpoints
- Use sampling to profile a percentage of requests
- Save profiling data to files instead of printing to console
- Combine with Prometheus metrics to identify endpoints worth profiling
Interpreting results
When reading pyinstrument output:- Total time: The overall time spent in the function and its children
- [self]: Time spent in the function itself, excluding child calls
- Hidden frames: Pyinstrument collapses internal library frames for clarity
- Call hierarchy: Indentation shows the call stack structure
Optimize based on profiling data
Use profiling results to guide optimization:- Focus on functions with high total time
- Look for unexpected bottlenecks
- Verify that optimizations reduce the measured time
- Profile before and after changes to measure improvement
Next steps
Prometheus metrics
Monitor metrics to identify endpoints to profile
Redis cache
Add caching to improve performance