simulation limitation occupation memory

bonsoir
appel aux petits génies du C :grin:

Je suis plus un homme de capteur qu’un homme du C ,
en discutant du projet de JF (à prendre là pour la compréhension de la problématique )

Je me suis aperçu qu’un tableau de type boolean occupe sous arduino 1 octet alors qu’un bit suffi(rait)t
Par envie de m’y coller , j’en suis arrivé à ça (code)
Il y a une approche plus “codeur” pour simplifier ? 8)

void setup() {
 Serial.begin(115200);

}

unsigned long ray=0;
unsigned long top=0;
byte pass=0;
void loop()
{
for (pass=0;pass <=254; pass ++) {  // pour simuler le taux d'acquisition
top = random(02);  // pour simulation occultation ou pas 
top = top << pass; // decalage du masque OR 
ray=ray | top; //OR 

}
//ecrire ray
Serial.println(ray, BIN); // pour voir les 0 et 1 
Serial.println(" ");
Serial.println(ray); // pas vraiment utile mais bon ! 

Serial.println("");


ray=0;  //raz et on repart pour un tour 

}

Yop Artouste

Artouste:
Je me suis aperçu qu’un tableau de type boolean occupe sous arduino 1 octet alors qu’un bit suffi(rait)t

Quelque soit le type de variable elle occupera toujours minimum 1 octet (1 emplacement mémoire).
Le type boolean n’est rien d’autre qu’un “unsigned char” normalement (en c++ seulement, le type boolean n’existe pas en c).

tu peux faire :

boolean b = 5;

En fait un boolean peux posséder deux états: vrai ou faux, mais dire “1=vrai” et “0=faux” n’est pas tout à fais exact, en fait la véritable définition d’un boolean en programmation devrait être : toute valeur différente de 0 (faux) est considéré comme vrai (1, 1.55555, -1, 255, -128, …).
Preuve :

if(238) {}
if(-66) {}
if(1.5555) {}
if(0) {} // sera la seule condition à ne jamais s'executer

J’utilise rarement le “type” boolean, si j’ai plusieurs états à stocker j’utilise 1 (ou plusieurs) octet ce qui me permet 8 états minimum (comme tu l’a fais avec l’unsigned long je pense ?).

Artouste:

for (pass=0;pass <=254; pass ++) {  // pour simuler le taux d'acquisition

top = random(02);  // pour simulation occultation ou pas
top = top << pass; // decalage du masque OR
ray=ray | top; //OR

Un truc me chiffonne dans “top = top << pass;”, pass s’incrémente jusque 254 or tu fais un décalage d’après “pass” donc jusqu’à (<< 254) sur top de 32bit donc normalement “<< 31” max.
Pour “random(02)” ce n’est pas plutôt “random(1)” → valeur max 1 (donc 0 ou 1) ?

Je ne sais pas si il faut obligatoirement 255 valeurs (0->254) mais pour un unsigned long (32bit) ceci devrait être bon.

for (byte pass=0;pass <=31; pass ++)
{  
  ray+=(random(1)<< pass); //addition binaire directement sur "ray" même d'après la valeur renvoyer par random décaler selon l'incrémentation.
}

Vue de ce qu’il ce passe niveau binaire (c’est identique au système décimal).

 0000 0001 //1 << 0
+0000 0010 //1 << 1
+0000 0000 //0 << 3
+0001 0000 //1 << 5
---------------
=0001 0011

Salut,

Peut-être une piste du côté des champs de bits. Extrait de la page bit field sur Wikipedia) :

Kernighan and Ritchie's book, The C Programming Language, describes a method for defining and accessing fields directly. Using this method, bitwise operators are not needed as bit members can be accessed the same as members of a structure without the need to create a object-oriented class like the one above. An example using C's struct keyword and C++'s bool data type follows:

typedef struct Preferences {
    bool likesIceCream : 1;
    bool playsGolf     : 1;
    bool watchesTv     : 1;
    bool readsBooks    : 1;
} Preferences; 

Preferences fred; fred.likesIceCream = true; fred.playsGolf    = true; fred.watchesTv    = true; fred.readsBooks    = false;

SesechXP: Salut,

Peut-être une piste du côté des champs de bits. Extrait de la page bit field sur Wikipedia)

+1

C'est une excellente technique pour optimiser l'occupation de la mémoire, d'ailleurs elle est utilisée (entre autres) dans le bootloader Arduino.

osaka:
Un truc me chiffonne dans “top = top << pass;”, pass s’incrémente jusque 254 or tu fais un décalage d’après “pass” donc jusqu’à (<< 254) sur top de 32bit donc normalement “<< 31” max.
Pour “random(02)” ce n’est pas plutôt “random(1)” → valeur max 1 (donc 0 ou 1) ?

Je ne sais pas si il faut obligatoirement 255 valeurs (0->254) mais pour un unsigned long (32bit) ceci devrait être bon.

for (byte pass=0;pass <=31; pass ++)

{  
 ray+=(random(1)<< pass); //addition binaire directement sur “ray” même d’après la valeur renvoyer par random décaler selon l’incrémentation.
}

Bonjour
oui pour le pass<=31 , je me suis rendu compte que j’avais fait une confusion après avoir éteint mon pc et comme ce coucher tard nuit !
:grin:
mon probleme se reduit à avoir une succession de 256 bit , il me faut donc 8 unsigned long (8X32=256)
en fait j’ai reglé le probleme en utilisant 2 boucles imbriquées for 7 to 0 pour ray et for 0 to 31 pour pass
pour le random selon la doc max est exclu et en test avec un 1 ça retourne que du 0 , on ne peut pas vraiment appeler ça du random :grin:
random(max)
random(min, max)
Parameters
min - lower bound of the random value, inclusive (optional)
max - upper bound of the random value, exclusive
Returns

a random number between min and max-1 (long)

ok pour l’addition binaire directe

Benvenuto:

SesechXP:
Salut,

Peut-être une piste du côté des champs de bits. Extrait de la page bit field sur Wikipedia)

+1

C’est une excellente technique pour optimiser l’occupation de la mémoire, d’ailleurs elle est utilisée (entre autres) dans le bootloader Arduino.

bonjour benvenuto
merci pour le lien , en fait c’est intuitivement ce que j’avais fait

je suis parti sur 8 unsigned long (8X32) , mais in fine 32 byte (8 bit) permettent sans déchet la même occupation mémoire pour le meme resultat

En ce qui concerne ensuite l’optimisation du process (temps d’exécution) et pour résultat identique
c’est préférable d’utiliser du byte ou du long ? , intuitivement je dirais byte gagnant ? , mais … ?

void setup() {
 Serial.begin(115200);

}

unsigned long ray[]={0,0,0,0,0,0,0,0};
unsigned long top=0;
byte pass=0;
byte ir=0;

void loop()
{
  for (ir=7;ir >0; ir --) {  // pour les 8 postes ray[] 7-->0 
// 1er bit top sur 1e LSB ray[7] dernier bit top sur le MSB ray[0] 
  
for (pass=0;pass <=31; pass ++) {  // pour simuler le taux d'acquisition
top = random(2);  // pour simulation occultation ou pas 
top = top << pass; // decalage du masque OR 

ray[ir]=ray[ir] | top; //OR 

}
  }
//ecrire ray
for (ir=7;ir >0; ir --) {
Serial.println(ray[ir], BIN); // pour voir les 0 et 1 
}
Serial.println("-------------------------------- ");

for (ir=7;ir >0; ir --) {
ray[ir]=0;  //raz et on repart pour un tour
}

}

Lorsqu'un programme utilise des types qui occupent plusieurs registres - typiquement des long - le compilateur traite et alloue les registres indépendamment les uns des autres ce qui permet de générer un code machine plus efficace.

Il y a une option -fno-split-wide-types qui permet de désactiver cette optimisation, mais elle n'est vraiment utile que pour débugger plus facilement l'assembleur généré et elle est inactive par défaut.

Donc en théorie les deux solutions devraient être équivalentes. Si tu as l'occasion de faire un comparatif, ça m'intéresse de savoir si c'est le cas en pratique :grin:

Benvenuto:
Lorsqu’un programme utilise des types qui occupent plusieurs registres - typiquement des long - le compilateur traite et alloue les registres indépendamment les uns des autres ce qui permet de générer un code machine plus efficace.

Il y a une option -fno-split-wide-types qui permet de désactiver cette optimisation, mais elle n’est vraiment utile que pour débugger plus facilement l’assembleur généré et elle est inactive par défaut.

Donc en théorie les deux solutions devraient être équivalentes. Si tu as l’occasion de faire un comparatif, ça m’intéresse de savoir si c’est le cas en pratique :grin:

merci pour l’option de désactivation , mais je la laisse là où je l’ai trouvée :grin:

par curiosite et sauf à avoir commis une bourde
sur un uno IDE 022
le 1er code 8 postes d’unsigned long renvoi ~1694 ms d’exécution pour 50 boucles
le 2 eme code 32 postes de byte renvoi ~1784 ms
j’ai volontairement squizzé les envois serial sauf en fin de boucle pour ne pas pas polluer avec des longueurs d’affichage différentes

1er code

void setup() {
  Serial.begin(115200);

}

unsigned long ray[]={0,0,0,0,0,0,0,0};
unsigned long top=0;
unsigned long act=0;
byte pass=0;
byte ir=0;
word ts=0;
void loop()
{
  act=millis();
  for (ts=0;ts<=50;ts ++) {
 

    for (ir=7;ir >0; ir --) {  // pour les 8 postes ray[] 7-->0 
      // 1er bit top sur 1e LSB ray[7] dernier bit top sur le MSB ray[0] 

      for (pass=0;pass <=31; pass ++) {  // pour simuler le taux d'acquisition
        top = random(2);  // pour simulation occultation ou pas 
        top = top << pass; // decalage du masque OR 

        ray[ir]=ray[ir] | top; //OR 

      }
    }
   
    for (ir=7;ir >0; ir --) {
      ray[ir]=0;  //raz et on repart pour un tour
    }
    
  }
  act=millis()-act;
    Serial.println(act,DEC); // pour voir les 0 et 1 
}

2eme code

void setup() {
 Serial.begin(115200);

}

byte ray[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
byte top=0;
unsigned long act=0;
byte pass=0;
byte ir=0;
word ts=0;

void loop()
{
 
 act=millis();
  for (ts=0;ts<=50;ts ++) { 
  for (ir=31;ir >0; ir --) {  // pour les 32 postes ray[] 31-->0 
// 1er bit top sur 1e LSB ray[7] dernier bit top sur le MSB ray[0] 
  
for (pass=0;pass <=7; pass ++) {  // pour simuler le taux d'acquisition
top = random(2);  // pour simulation occultation ou pas 
top = top << pass; // decalage du masque OR 

ray[ir]=ray[ir] | top; //OR 

}
  }



for (ir=31;ir >0; ir --) {
ray[ir]=0;  //raz et on repart pour un tour
}

}
act=millis()-act;
    Serial.println(act,DEC); // pour voir les 0 et 1 
}

Artouste: ``` byte ray[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

ray[ir]=ray[ir] | top; //OR

Yop Artouste,

Juste au cas ou, parmis les nombreux raccourcis de programmeur fainéant:

byte ray[32]={0}; // les 32 emplacements réservés seront bien tous initialisé à 0.

ray[ir] |= top; //équivalent à "ray[ir]=ray[ir] | top;"

osaka:

Artouste: ``` byte ray[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

ray[ir]=ray[ir] | top; //OR



Yop Artouste,

Juste au cas ou, parmis les nombreux raccourcis de programmeur fainéant:

byte ray[32]={0}; // les 32 emplacements réservés seront bien tous initialisé à 0.

ray[ir] |= top; //équivalent à "ray[ir]=ray[ir] | top;"

bonjour osaka pas d'accord , je prend ça pour du codage fait par un programmeur aguerri, pas feignant, ce que très humblement je revendique ne pas être dans les 2 cas ! :grin: mais c'est vrai que la concision est agréable à regarder !

j'ai fais mes premières armes il y a déjà longtemps avec fortran, je ne suis même plus sur de savoir encore coder du simple avec !

Mais comme avec tous les langages , la syntaxe sans reflechir déjà à ce que l'on veut, quel que soit le langage ça ne sert pas à grand chose.

intellectuellement, je suis un peu surpris du résultat, intuitivement je donnais byte gagnant ! comme quoi il faut toujours se méfier des a priori .

ça s'explique très surement en regardant ce que le compilo génère dans les 2 cas. Mais là je délègue le challenge à ceux qui veulent s'y coller :grin:

Artouste:
pas d’accord , je prend ça pour du codage fait par un programmeur aguerri, pas feignant, ce que très humblement je revendique ne pas être dans les 2 cas ! :grin:
mais c’est vrai que la concision est agréable à regarder !

Tu n’imagines pas tout ce que le programmeur est près à faire pour s’épargner une ligne de code. :grin:
Par contre certain raccourcis ou facilitées ne me plaise guère tel que :
bloc sans accolades:

if(condition)
   blabla code;

for(int i = 0; i < blabla; i++)
   blabla code;

accolade décalées:

if(condition){
   blabla code;
}

Qui nuisent à la bonne lecture et propreté du code pour moi. =(
Ou également le typage dynamique fort tels qu’on vois dans certain langages comme python, php, … (la raison de leurs succès ?)

Artouste:
Mais comme avec tous les langages , la syntaxe sans reflechir déjà à ce que l’on veut, quel que soit le langage ça ne sert pas à grand chose.

Le premier outil du développeur: papier, crayon, cerveau (je suis mal barré). :grin:

Artouste:
intellectuellement, je suis un peu surpris du résultat, intuitivement je donnais byte gagnant ! comme quoi il faut toujours se méfier des a priori .

Je suis étonné également et n’empêche ce sont des choses auquel je n’aurais jamais pensé (ou même cherché) en développant sur pc classique. :blush:
:wink:

osaka: Je suis étonné également et n’empêche ce sont des choses auquel je n'aurais jamais pensé (ou même cherché) en développant sur pc classique. :blush: ;)

d'autant que toujours par curiosité je viens de regarder

Binary sketch size: 2658 bytes (of a 30720 byte maximum) pour le 8 unsigned long Binary sketch size: 2598 bytes (of a 30720 byte maximum) pour le 32 byte

le code le plus "compact" est (serait) le plus "lent" en exécution 8)

Artouste: d'autant que toujours par curiosité je viens de regarder

Binary sketch size: 2658 bytes (of a 30720 byte maximum) pour le 8 unsigned long Binary sketch size: 2598 bytes (of a 30720 byte maximum) pour le 32 byte

le code le plus "compact" est (serait) le plus "lent" en exécution 8)

Alors là je suis encore plus étonné :astonished:. Comme quoi il vaut mieux une longue boucle avec de petite données qu'une petite avec de grosses. (le mythe est tombé) :grin:

osaka:

Artouste:
d’autant que toujours par curiosité je viens de regarder

Binary sketch size: 2658 bytes (of a 30720 byte maximum) pour le 8 unsigned long
Binary sketch size: 2598 bytes (of a 30720 byte maximum) pour le 32 byte

le code le plus “compact” est (serait) le plus “lent” en exécution 8)

Alors là je suis encore plus étonné :astonished:.

tu a testé les 2 bouts de code ?
En bon scientifique
si des participants du forum veulent bien vérifier et tester les 2 bouts de codes pour levée de doute (ou débusquer incohérence)
size time ?

Je ferais quelques testes tantôt (je dois aller faire les courses :grin:), mais parfois j’ai constaté dans mes codes une augmentation en mémoire programme alors que je pensais faire le contraire … :~
Mystère du compilateurs ?
Un poste récent montrait une incrémentation de 2 plus lourde que de 3. :grin:

Ai fait le test avec un résultat identique aux tiens.
Voilà comment je vois ton développement:

byte ray[32]={0};
unsigned long act=0;

void setup() 
{
  Serial.begin(115200);
}

void loop()
{
  act=millis();
  for (byte ts=0;ts<=50;ts++) 
  { 
    for (byte ir=31;ir >0; ir--) 
    {   
      for (byte pass=0;pass <=7; pass ++) 
      {
        ray[ir] += random(2) << pass; 
      }
    }
  }
  
  Serial.println(millis()-act,DEC);
  delay(1000);
}

Le raz était obligatoire ?

osaka: Ai fait le test avec un résultat identique aux tiens. Voilà comment je vois ton développement:

Le raz était obligatoire ?

pour le raz je l'avais mis par "securité , mais c'est plus là une demarche intellectuelle qu'un réel projet..

avec ton (tes) code c'est etonnant

ta version en 32 byte/ la mienne renvoi une légère diminution du temps de boucle 1780 ms contre 1784 en revanche l'occupation passe à 2640 contre 2598

ta version que j'ai passé en 8 unsigned long/ la mienne idem 1678 ms de boucle contre 1694 (diminution du temps de boucle + intéressant), mais là aussi l'occupation passe de 2752 contre 2658.

etonnant , mais interessant

Yop yop,
Ai fais les même testes, je les repostes histoire d’avoir les même versions.

Version byte ray[] :

void setup() 
{
 Serial.begin(115200);
}

byte ray[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
byte top=0;
unsigned long act=0;
byte pass=0;
byte ir=0;
word ts=0;

void loop()
{
  act=millis();
  for (ts=0;ts<=50;ts ++) 
  { 
    for (ir=31;ir >0; ir --) 
    {
      for (pass=0;pass <=7; pass ++) 
      {
        top = random(2); 
        top = top << pass; 
        
        ray[ir]=ray[ir] | top; 
      }
    }
  }
  act=millis()-act;
  Serial.println(act,DEC);
}
byte ray[32]={0};
unsigned long act=0;

void setup() 
{
  Serial.begin(115200);
}

void loop()
{
  act=millis();
  for (byte ts=0;ts<=50;ts++) 
  { 
    for (byte ir=31;ir >0; ir--) 
    {   
      for (byte pass=0;pass <=7; pass ++) 
      {
        ray[ir] += random(2) << pass; 
      }
    }
  }
  
  Serial.println(millis()-act,DEC);
//  delay(1000);
}

2572/2528 bytes (of a 30720 byte maximum)
1810/1807 ms

Version long ray[]:

void setup() 
{
  Serial.begin(115200);
}

unsigned long ray[]={0,0,0,0,0,0,0,0};
unsigned long top=0;
unsigned long act=0;
byte pass=0;
byte ir=0;
word ts=0;

void loop()
{
  act=millis();
  for (ts=0;ts<=50;ts ++) 
  {
    for (ir=7;ir >0; ir --) 
    {
      for (pass=0;pass <=31; pass ++) 
      {
        top = random(2); 
        top = top << pass;

        ray[ir]=ray[ir] | top; 

      }
    }  
  }
  act=millis()-act;
  Serial.println(act,DEC);
}
unsigned long ray[8]={0};
unsigned long act=0;

void setup() 
{
  Serial.begin(115200);
}

void loop()
{
  act=millis();
  for (byte ts=0;ts<=50;ts ++) 
  {
    for (byte ir=7;ir >0; ir --) 
    {
      for (byte pass=0;pass <=31; pass ++) 
      {
        ray[ir] += random(2) << pass;  
      }
    } 
  }
  act=millis()-act;
  Serial.println(act,DEC);
}

2620/2578 bytes (of a 30720 byte maximum)
1718/1702 ms

Conclusion:

Je dirais que la plus grosse différence pour la taille en mémoire programme entre ta version et la mienne est l’utilisation de variable globale ou locale et niveau temps d’exécution la différence ce situe aux niveau du passage par une variable intermédiaire “top” dans la dernière boucle ?

Par contre la différence entre l’utilisation de byte et long vient peux être de là ???

Benvenuto:
Lorsqu’un programme utilise des types qui occupent plusieurs registres - typiquement des long - le compilateur traite et alloue les registres indépendamment les uns des autres ce qui permet de générer un code machine plus efficace.

osaka: Yop yop, Ai fais les même testes, je les repostes histoire d'avoir les même versions.

Par contre la différence entre l'utilisation de byte et long vient peux être de là ???

Benvenuto: Lorsqu'un programme utilise des types qui occupent plusieurs registres - typiquement des long - le compilateur traite et alloue les registres indépendamment les uns des autres ce qui permet de générer un code machine plus efficace.

Tres humblement, cela dépasse mon niveau en prog , mais c'est intéressant de savoir qu'un code compact n'est pas nécessairement synonyme de rapidité. Selon l'optimisation que l'on souhaite et si on est en limite, ça peut être à prendre en compte. Ce qui n'est pas là mon cas :grin:

Benvenuto: le compilateur traite et alloue les registres indépendamment les uns des autres.

En fait je suppose que c'est du au fait de la largeur des registre (8bit) ? Maintenant pour resituer se type (long/32bit) il y a des opérations à effectuer en plus (décalage, ...) ? Traitement dont byte (unsigned char) n'a pas besoin. Enfin c'est une supposition parce que là ça dépasse également mon niveau question fonctionnement microcontrôleur, registres , ... :grin:

Benvenuto: permet de générer un code machine plus efficace.

C'est un peux contradictoire non ?

osaka:

Benvenuto: permet de générer un code machine plus efficace.

C'est un peux contradictoire non ?

non , pas de contradiction tout dépend de ce que l'on entend par efficacité : Savoir pour un codeur, comment bien optimiser l'occupation (taille code) Vs rapidité (d’exécution) selon les impératifs, est une demonstration d'efficacité. :grin: