Les Crash-Devs d'un Passionné

Mettre en place du profiling CPU et RAM dans votre code

/Catégorie/python

Temps de lecture : 5 minutes

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
serait considéré comme les arguments de la commande
 scalene

Point 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

clipboard
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
qui contient la donnée brute nécessaire à la génération du rapport html

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)

clipboard
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.

clipboard
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

clipboard
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()
permet d'arrêter la collecte d'informations par scalene. Cependant, cette unique méthode ne génère pas le rapport (configuré en JSON et HTML dans mon wrapper vu au début de l'article), il faut explicitement lui indiquer par
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
ou
--html
dans le wrapper du fichier qui ne vous intéresse pas.

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

scalene_ui_result

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.