Il existe plusieurs outils permettant de surveiller les ressources de votre application, l'idée de cet article est de montrer comment utiliser scalene pour identifier dans le code à la ligne prêt ce qui consomme du temps CPU ou de la RAM.
Nous allons commencer par installer la librairie
pip install scalene
On commence par mettre un wrapper lors du démarrage du serveur d'application
poetry run scalene --- -m uvicorn src.main:app --host 0.0.0.0 --port 8001
Les 3 tirests sont important, c'est un séparateur d'argement. Sans cette syntaxe les options
-m uvicorn src.main:app --host 0.0.0.0 --port 8001
scalenePoint important, si vous démarrer votre application avec un serveur configurer avec l'auto reload comme
fastapi dev /app/src/main.py --host=0.0.0.0 --port=8001
Cela perturbe les signaux d'interruption applicatif et comme scalene se connecte dessus pour faire le profiling la récolte des données ou la génération des rapports peut être éronné.
Pour ma part je suis partir sur un script que j'ai ajouté dans mon docker compose
command: sh -c "poetry run scalene scripts/profile/run_with_scalene.py"
Avec comme contenu
Copier le code
1 2 3 4 import uvicorn if __name__ == "__main__": uvicorn.run("src.main:app", host="0.0.0.0", port=8001)
Et afin d'avoir un rapport exploitable, je veux générer le rapport html donc je rajoute ces options
poetry run scalene --json --html --outfile profile.html scripts/profile/run_with_scalene.py
Maintenant que votre application est prête à récolter les données, il faut indiquer à scalene d'interrompre l'analyse et de générer le rapport. Par défaut, il attend un signal SIGTERM pour créer le fichier html.
Point important pour générer le fichier html il faut également mettre l'option
--json
Donc nous allons arrêter notre application pour générer ce rapport. Comme je suis sur Docker, je dois expliciterment indiquer un SIGTERM car Docker génère par défaut un SIGKILL qui n'est pas capturé par Scalene. Pour cela j'ai ajouter une route (syntaxe pour une application FastApi)
Copier le code
1 2 3 @app.get("/stop") async def stop_profile(): os.kill(os.getpid(), signal.SIGTERM)
En local, votre application s'arrêtera et cela ne posera pas de problème, mais si vous voulez lancer Scalene sur un autre environnement, ce n'est pas la meilleure solution.
Nous allons donc préciser dans le code des instructions pour démarrer et arrêter le profiling.
Copier le code
1 2 3 4 5 6 7 8 9 from scalene import scalene_profiler # Turn profiling on scalene_profiler.start() # your code # Turn profiling off scalene_profiler.stop()
On peut imaginer le démarrage du profiling au démarrage de l'application , et pour l'arrêter on configure un endpoint (ce n'est qu'une suggestion il dépend de ce que vous voulez mesurer)
Dans une application fastapi on aurait
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from scalene import scalene_profiler from scalene.scalene_output import ScaleneOutput app = FastAPI() scalene_profiler.start() @app.get("/stop") async def stop_profile(): scalene_profiler.stop() scalene_profiler.Scalene.output_profile() return {"status": "Profiling stopped, report generated"}
La méthode
stop()scalene_profiler.Scalene.output_profile()Il est important de savoir que cette méthode exécute soit un JSON soit un HTML mais pas les deux. Il faut supprimer l'option
--json
--html
Ainsi, après avoir appelé /stop le fichier html ou json sera généré.
Pour lire le fichier profile il vous suffit de lancer sur votre poste l'UI avec scalene view profile.json, une fenetre s'ouvre dans votre navigateur. Il faudra bien évidemennet que scalene soit installé
Sinon vous pouvez simplement executer le fichier profile.html (avec l'option --html sur le wrapper) qui donnera le même résultat sans avoir à installer scalene

Pour la démo j'ai exécuté des fonctions volontairement consommatrices de CPU et RAM pour mettre en avant les métriques que l'application révèle.
Pour l'exemple je n'ai pas sollicité le GPU mais le principe reste le même, car scalene peut également monitorer cela.