Mesurer la Flash et la RAM occupées par un modèle sur une ESP32-S3, ainsi que la durée d'une inférence, pour comparer deux versions d'un modèle.
Mesurer la mémoire et le temps d'inférence
Mesurer la Flash et la RAM occupées par un modèle sur une ESP32-S3, ainsi que la durée d'une inférence, pour comparer deux versions d'un modèle.
Pré-requis :
Un modèle qui marche sur l’ordinateur peut être trop gros pour la carte, ou trop lent pour réagir à temps. Avant de choisir entre deux versions (plus de couches, quantifié ou non), on mesure trois choses : la Flash qu’il occupe, la RAM dont il a besoin pour calculer, et le temps d’une inférence.
À chaque compilation, PlatformIO affiche l’occupation de la mémoire :
RAM: [== ] 15.2% (used 49812 bytes from 327680 bytes)
Flash: [=== ] 28.4% (used 949201 bytes from 3342336 bytes)
static, dont la zone de travail du modèle si elle est déclarée en global.Compilez une fois sans le modèle, une fois avec : la différence est ce que le modèle coûte.
La compilation ne voit pas la mémoire réservée pendant l’exécution (le tas). Affichez-la depuis le programme, avant et après le chargement du modèle :
Serial.printf("Tas libre : %lu octets\n", (unsigned long)ESP.getFreeHeap());
Serial.printf("Plus bas atteint depuis le démarrage : %lu octets\n", (unsigned long)ESP.getMinFreeHeap());
ESP.getMinFreeHeap() est le chiffre qui compte : le minimum atteint depuis le démarrage. S’il descend sous quelques dizaines de kilo-octets, le Wi-Fi ou une String peuvent suffire à faire planter la carte.
Entourez l’appel du modèle de deux lectures de micros(), et faites la moyenne sur plusieurs appels : la première inférence est souvent plus lente que les suivantes.
constexpr int RUNS = 50;
uint32_t start = micros();
for (int i = 0; i < RUNS; i++) {
runInference(); // remplacer par l'appel réel du modèle
}
unsigned long averageUs = (micros() - start) / RUNS;
Serial.printf("Inférence : %lu µs en moyenne\n", averageUs);
Comparez ce temps à votre contrainte : pour des gestes échantillonnés toutes les 2 secondes, 50 ms est largement suffisant ; pour un jeu cadencé à 16 ms par image, il faut que l’inférence tienne dans ce budget, ou qu’elle tourne sur l’autre cœur.
Consignez chaque version dans un tableau, dans votre journal de bord :
| Version du modèle | Flash | RAM (minimum libre) | Inférence | Précision |
|---|---|---|---|---|
| float32 | ||||
| int8 (quantifié) |
C’est ce tableau qui justifie le choix final, à l’écrit comme à l’oral.
| Symptôme | Cause probable | Solution |
|---|---|---|
region 'dram0_0_seg' overflowed à la compilation |
Trop de variables globales, ou zone de travail du modèle trop grande | Réduire la zone de travail, ou la placer en PSRAM |
| La carte redémarre au chargement du modèle | Plus assez de tas | Vérifier ESP.getMinFreeHeap(), éviter les String, quantifier le modèle |
| Temps d’inférence très variable | Le Wi-Fi interrompt le calcul | Mesurer Wi-Fi coupé, puis Wi-Fi allumé, et garder le pire cas |
| Le temps affiché vaut 0 | Une seule inférence, plus courte qu’une microseconde mesurable, ou optimisée par le compilateur | Faire la moyenne sur plusieurs appels, utiliser le résultat de l’inférence |