Go Down

Topic: simulation limitation occupation memory (Read 1 time) previous topic - next topic

Artouste

bonsoir
appel aux petits génies du C  :smiley-mr-green:

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 )
http://arduino.cc/forum/index.php/topic,89413.msg708521.html#msg708521

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)
Code: [Select]

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

}


osaka

#1
Mar 01, 2012, 12:41 am Last Edit: Mar 01, 2012, 01:00 am by osaka Reason: 1
Yop 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 :
Code: [Select]

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 :

Code: [Select]

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 ?).


Code: [Select]

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.
Code: [Select]

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).
Code: [Select]

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

SesechXP

Salut,

Peut-être une piste du côté des champs de bits. Extrait de la page bit field sur Wikipedia) :
Quote
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:
Code: [Select]
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;

Benvenuto


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.

Artouste

#4
Mar 01, 2012, 12:03 pm Last Edit: Mar 01, 2012, 02:18 pm by Artouste Reason: 1


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.
Code: [Select]

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 !
:smiley-mr-green:
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  :smiley-mr-green:
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



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


Code: [Select]
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
}

}


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  :smiley-mr-green:

Artouste


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  :smiley-mr-green:

merci pour l'option de désactivation , mais je la laisse là où je l'ai trouvée  :smiley-mr-green:

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
Code: [Select]
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
Code: [Select]
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
}


osaka


Code: [Select]
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:

Code: [Select]

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

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

Artouste



Code: [Select]
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:

Code: [Select]

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 !   :smiley-mr-green:
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  :smiley-mr-green:

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 !   :smiley-mr-green:
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.  :smiley-mr-green:
Par contre certain raccourcis ou facilitées ne me plaise guère tel que :
bloc sans accolades:
Code: [Select]

if(condition)
   blabla code;

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


accolade décalées:
Code: [Select]

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 ?)


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é).  :smiley-mr-green:


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.   :smiley-red:
;)

Artouste



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.   :smiley-red:
;)

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)



osaka


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é  :smiley-eek:.
Comme quoi il vaut mieux une longue boucle avec de petite données qu'une petite avec de grosses. (le mythe est tombé)  :smiley-mr-green:

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é  :smiley-eek:.
...

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 ?



osaka

Je ferais quelques testes tantôt (je dois aller faire les courses  :smiley-mr-green:), 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.  :smiley-mr-green:

osaka

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

Code: [Select]

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 ?

Go Up