Skip to content

Profiling

Idea

Profiling is a technique to figure out how time is spent in a program. With these statistics, we can find the “hot spot” of a program and think about ways of improvement. Sometimes, a hot spot in an unexpected location may also hint at a bug in the program.

Pyinstrument is a Python profiler. A profiler is a tool to help you optimize your code - make it faster.

Profile a web request in FastAPI

To profile call stacks in FastAPI, you can write a middleware extension for pyinstrument.

Create an async function and decorate it with app.middleware('http') where the app is the name of your FastAPI application instance.

Make sure you configure a setting to only make this available when required.

from pyinstrument import Profiler


PROFILING = True  # Set this from a settings model

if PROFILING:
    @app.middleware("http")
    async def profile_request(request: Request, call_next):
        profiling = request.query_params.get("profile", False)
        if profiling:
            profiler = Profiler(interval=settings.profiling_interval, async_mode="enabled")
            profiler.start()
            await call_next(request)
            profiler.stop()
            return HTMLResponse(profiler.output_html())
        else:
            return await call_next(request)

To invoke, make any request to your application with the GET parameter profile=1 and it will print the HTML result from pyinstrument.

AuthX's Support

With AuthX the abstract of profiling is easy, it's just about calling the ProfilerMiddleware 's class and calling it in add_middleware(ProfilerMiddleware) func that FastAPI provides.

Example

import os
import uvicorn

from fastapi import FastAPI
from fastapi.responses import JSONResponse

from authx import ProfilerMiddleware


app = FastAPI()
app.add_middleware(ProfilerMiddleware)


@app.get("/test")
async def normal_request():
    return JSONResponse({"retMsg": "Hello World!"})


if __name__ == '__main__':
    app_name = os.path.basename(__file__).replace(".py", "")
    uvicorn.run(app=f"{app_name}:app", host="0.0.0.0", port=8080, workers=1)

References