Alors voilà : comme je suis curieux et que j'aime bien aller au fond des choses pour mieux les maîtriser, je me suis livré à une petite expérience dont je ne comprends pas le résultat.
Cela concerne l'utilisation de variables locales en static.
Comme point de départ, je me suis dit : puisque mon arduino est un environnement d'exécution de programme fermé, pourquoi chercher à économiser de la RAM lorsque ce n'est pas nécessaire ?
Par exemple, si la taille cumulée de toutes mes variables internes représente moins de 1000 octets, l'allocation / désallocation dynamique de mémoire à chaque entrée / sortie d'une fonction est parfaitement inutile. Dans ce cas, cette gestion dynamique alourdit certainement le code généré, et diminue les performances à l'exécution.
J'ai donc fait un test pour mieux mesurer les impacts de l'utilisation de static
//programme d'évaluation des conséquences d'une déclaration des variables locales en static
//à exécuter avec la ligne ci-dessous active, puis commentée,
//et comparer les résultats, y compris la taille du code compilé
#define static_on
uint8_t bidon(uint8_t a)
{
#ifdef static_on
static uint8_t c,d,i;
static uint8_t buffer[16];
#else
uint8_t c,d,i;
uint8_t buffer[16];
#endif
//ne cherchez pas la logique des instructions ci-dessous : il n'y en a aucune
c = a + 5;
if (c&1)
{
d = a + c;
}
else
{
d = a - c;
}
for (i=0;i<16;i++)
{
if (c>d+i)
{
buffer[i] = d;
}
else
{
buffer[i] = c;
}
}
d=0;
for (i=0;i<16;i++)
{
d += buffer[i];
}
return d;
}
int freeRam ()
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
void setup()
{
const uint32_t imax=100000;
uint32_t i, chrono;
Serial.begin(9600);
Serial.print("Execution avec les variables locales allouees en ");
#ifdef static_on
Serial.println("statique ");
#else
Serial.println("dynamique");
#endif
Serial.print("Ram disponible : ");
Serial.print(freeRam());
Serial.println(" octets");
Serial.print("Test de performance sur ");
Serial.print(imax);
Serial.println(" iterations...");
chrono = millis();
for (i=0;i<imax;i++)
{
bidon((uint8_t) i);
}
chrono = millis() - chrono;
Serial.print("Temps d'execution total : ");
Serial.print(chrono);
Serial.println(" millisecondes");
chrono = (chrono * 1000) / imax;
Serial.print("Temps d'execution moyen : ");
Serial.print(chrono);
Serial.println(" microsecondes");
}
void loop()
{
delay(5000);
}
Avec pour résultat :
Taille binaire du croquis : 3 282 octets
Execution avec les variables locales allouees en dynamique
Ram disponible : 1623 octets
Test de performance sur 100000 iterations...
Temps d'execution total : 2716 millisecondes
Temps d'execution moyen : 27 microsecondes
Taille binaire du croquis : 3 268 octets
Execution avec les variables locales allouees en statique
Ram disponible : 1604 octets
Test de performance sur 100000 iterations...
Temps d'execution total : 3322 millisecondes
Temps d'execution moyen : 33 microsecondes
Conclusion :
- l'écart de RAM disponible au lancement du programme (19 octets) correspond bien aux variables déclarées en static.
- le code binaire généré est bien un poil plus concis, probablement lié au non appel de routines d'allocation / désallocation mémoire
MAIS : en static le programme est significativement plus lent à l'exécution !?
Donc en gros en static le programme fait moins de choses, mais met plus de temps pour le faire.
Et là je sèche...
Une idée ?