risparmiare ram

?R:
approposito dei define, nel reference c'è scritto che è meglio usare const, ma alla fine è lo stesso no?

Da prove che ho fatto in passato, usare un #define o un const byte per un pin è del tutto equivalente. Il compilatore ottimizza e l'impegno di ram finale dello sketch è identico sia usando la prima che la seconda soluzione.

Su questo sketch:

#define PIN 13

void setup() {
    pinMode(PIN, OUTPUT);
    digitalWrite(PIN, HIGH);
}

void loop() {
}

avr-size dà il seguente responso: 876 di flash e 9 byte di ram.

Su questo sketch:

const byte PIN = 13;

void setup() {
    pinMode(PIN, OUTPUT);
    digitalWrite(PIN, HIGH);
}

void loop() {
}

avr-size dà gli stessi identici risultati: 876 byte di flash e 9 byte di ram.

Invece senza specificare "const":

byte PIN = 13;

void setup() {
    pinMode(PIN, OUTPUT);
    digitalWrite(PIN, HIGH);
}

void loop() {
}

avr-size dice che lo sketch occupa 882 byte di flash e 11 byte di ram.

ok grazie delle conferme, magari tra qualche periodo provo anch'io quei programmi di avr che usate voi, sono cose molto interessanti :wink:

?R:
oggi in questo topic http://arduino.cc/forum/index.php/topic,135034.0.html era venuto fuori il discorso della dimensione delle variabili, è giusto quello che dico lì? anche quello influisce sulla ram no?

Veramente li parlavamo di spazio occupato dalla SRAM non RAM dove vengono allocate le variabili a meno che per ram non intendevi sram, che nel caso della UNO è di 2k nella MEGA invece 8k

pablos:
Veramente li parlavamo di spazio occupato dalla SRAM non RAM dove vengono allocate le variabili a meno che per ram non intendevi sram, che nel caso della UNO è di 2k nella MEGA invece 8k

RAM (Random Access Memory) è il termine generico per indicare la memoria di un micro dove puoi sia leggere che scrivere e accedere liberamente ad una qualunque singola cella, SRAM e DRAM sono due termini che identificano la tecnologia della RAM, SRAM = Static RAM, DRAM = Dynamic RAM, però sempre di RAM si tratta e svolge la stessa funzione.

leo72:
Da prove che ho fatto in passato, usare un #define o un const byte per un pin è del tutto equivalente. Il compilatore ottimizza e l'impegno di ram finale dello sketch è identico sia usando la prima che la seconda soluzione.

Vero però c'è una differenza sostanziale tra i due metodi, se usi un const viene creata una variabile di tipo costante sfruttando la flash, se usi #define viene sostituito il valore in fase di compilazione direttamente nell'espressione.
Utilizzando const il codice è di questo tipo

const byte pin 13;
digitalWrite(pin, HIGH);

Utilizzando "#define pin 13" la digitalWrite, dopo la compilazione, diventa equivalente a:

digitalWrite(13, HIGH);

Anche se la differenza può sembrare minima, quasi inutile, in realtà cambia molto a livello di run time perché nel primo caso il codice deve indirizzare una variabile per recuperare il valore di pin, nel secondo caso il valore è subito disponibile nel codice assembly senza doverlo recuperare, questo si traduce in diversi cicli macchina risparmiati e di conseguenza un codice più veloce.
Da notare che per accedere ad un valore posto nella flash è necessario utilizzare l'istruzione LPM (load Program Memory) che aggiunge un ulteriore overhead di cicli macchina per il recupero del valore di una variabile.

questo codice...

#define BOO 3

const byte bii = 2;

byte res;
byte ciccio;
void setup() {
 
 ciccio = BOO; 
 res = bii;
}

void loop() {
   
  ciccio++;
  res++;
 
}

viene tradotto così:

00000000 <setup>:
   0:   83 e0           ldi     r24, 0x03       ; 3    <-- ciccio = BOO (DEFINE)
   2:   80 93 00 00     sts     0x0000, r24
   6:   82 e0           ldi     r24, 0x02       ; 2     <-- res = bii (const)
   8:   80 93 00 00     sts     0x0000, r24
   c:   08 95           ret

Disassembly of section .text.loop:

00000000 <loop>:
   0:   80 91 00 00     lds     r24, 0x0000
   4:   8f 5f           subi    r24, 0xFF       ; 255
   6:   80 93 00 00     sts     0x0000, r24
   a:   80 91 00 00     lds     r24, 0x0000
   e:   8f 5f           subi    r24, 0xFF       ; 255
  10:   80 93 00 00     sts     0x0000, r24
  14:   08 95           ret

ho dovuto complicarlo un po' perchè questo compilatore non è proprio tanto rinc0glionito... fa delle ottimizzazioni non esattamente scontate... infatti pensavo di aver capito tutto ed invece non ho capito proprio nulla...
allora lasciando stare la parte dove l'unica cosa curiosa è che per incrementare di 1 un byte lui toglie 255 :astonished: (...sti c@zzi) si vede che le due assegnazioni che ho fatto in setup sono tradotte pari pari.... quindi nessun risparmio in questo caso... quindi il mio messaggio precedente è del tutto errato: tanto per cambiare.

astrobeed, ciò è vero su un PC ma avr-gcc compila ottimizzando il codice ed i 2 listati sono trasformati nello stesso identico sorgente assembly. Il compilatore vede appunto che la variabile PIN è una const e la tratta nello stesso identico modo di una #define.

Avevo già fatto a suo tempo delle prove, le ho rifatte stamani con i 2 listati che ho pubblicato qualche post prima: "define.txt" contiene la versione assembly dello sketch che usa la #define, "const.txt" quella invece che usa la const byte.
Risultato di diff eseguito su di essi:

[leo@desktop-hp build8853307674189123112.tmp]$ diff define.txt const.txt -s
I file define.txt e const.txt sono identici

const.txt (13.4 KB)

qsecofr:
ho dovuto complicarlo un po' perchè questo compilatore non è proprio tanto rinc0glionito... fa delle ottimizzazioni non esattamente scontate...

Non è semplice, anche con poche righe di codice, capire cosa combina il compilatore e come ottimizza, spesso e volentieri il solo modo per verificare rapidamente se c'è stato un guadagno, o una perdita, di velocità sul nostro codice è fare delle misure strumentali.
Più tardi faccio un paio di prove, mirate a rilevare la reale differenza in cicli macchina tra l'uso di const e define, con l'utilizzo dell'analizzatore di stati logici in modo da poter misurare con precisione l'effettiva differenza.

leo72:
astrobeed, ciò è vero su un PC ma avr-gcc compila ottimizzando il codice ed i 2 listati sono trasformati nello stesso identico sorgente assembly. Il compilatore vede appunto che la variabile PIN è una const e la tratta nello stesso identico modo di una #define.

No, non è così, a parte che pure sul pc valgono le stesse regole e i compilatori ottimizzano sempre e comunque, certo sul pc non hai la flash con tutte le relative problematiche, però rimane il fatto che una const è una variabile e la define è una sostituzione operata dal compilatore con relative differenze nei metodi di indirizzamento.
Comunque lasciamo la parola definitiva agli strumenti, che non mentono mai, e vediamo cosa viene fuori dal test pratico, dovrei riuscire a farlo in giornata.

astrobeed:
Non è semplice, anche con poche righe di codice, capire cosa combina il compilatore e come ottimizza, spesso e volentieri il solo modo per verificare rapidamente se c'è stato un guadagno, o una perdita, di velocità sul nostro codice è fare delle misure strumentali.
Più tardi faccio un paio di prove, mirate a rilevare la reale differenza in cicli macchina tra l'uso di const e define, con l'utilizzo dell'analizzatore di stati logici in modo da poter misurare con precisione l'effettiva differenza.

Ti ho appena mostrato che i 2 sketch il compilatore li trasforma nello stesso identico codice macchina.
Sul microcontrollore ci sarà quindi lo stesso binario, giusto? Quindi il comportamento è lo stesso.

leo72:
Risultato di diff eseguito su di essi:

[leo@desktop-hp build8853307674189123112.tmp]$ diff define.txt const.txt -s

I file define.txt e const.txt sono identici

usando define però non posso usare PROGMEM, no?

?R:
usando define però non posso usare PROGMEM, no?

#define x y istruisce il compilatore a sostituire nel testo tutte le corrispondenze di "x" con "y", PROGMEM serve invece a dire al compilatore che i dati sono da prelevare dalla Flash.
Con #define non cambi solo dei valori ma puoi anche sostituire ad una parola chiave un qualcosa d'altro. Ad esempio PROGMEM è un alias creato con #define:

#define PROGMEM   __ATTR_PROGMEM__

che può essere poi riutilizzato:

#define PSTR	( s )   ((const PROGMEM char *)(s))

leo72:
Con #define non cambi solo dei valori ma puoi anche sostituire ad una parola chiave un qualcosa d'altro.

Con la define puoi anche creare delle macro, ovvero del codice vero e proprio collegato ad una parola chiave, p.e. cbi() e sbi() (clear bit e set bit) che vengono implementate tramite define:

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

astrobeed:

qsecofr:
ho dovuto complicarlo un po' perchè questo compilatore non è proprio tanto rinc0glionito... fa delle ottimizzazioni non esattamente scontate...

Non è semplice, anche con poche righe di codice, capire cosa combina il compilatore e come ottimizza, spesso e volentieri il solo modo per verificare rapidamente se c'è stato un guadagno, o una perdita, di velocità sul nostro codice è fare delle misure strumentali.
Più tardi faccio un paio di prove, mirate a rilevare la reale differenza in cicli macchina tra l'uso di const e define, con l'utilizzo dell'analizzatore di stati logici in modo da poter misurare con precisione l'effettiva differenza.

si be ma l'assembler non mente... le due assegnazioni vengono tradotte proprio uguale come ha confermato il mio codice e anche leo. Ora si potrebbe discquisire se questi adeguamenti avvengono sempre in modo uguale oppure delle volte le costanti vengono effettivamente memorizzate in ram come sarebbe logico supporre (ma che non avviene).
...comunque veramente incredibile cosa fa il compilatore... se fai di seguito ciccio = b; ciccio = c;... toglie proprio ciccio = b;

qsecofr:
Ora si potrebbe discquisire se questi adeguamenti avvengono sempre in modo uguale oppure delle volte le costanti vengono effettivamente memorizzate in ram come sarebbe logico supporre (ma che non avviene).

Dipende molto anche dal livello di ottimizzazione impostato e se viene scelto di ottimizzare la velocità piuttosto che l'occupazione di memoria, sono due cose che spesso e volentieri sono in contrasto tra loro.
Detto in altri termini, non è detto che il compilatore faccia la stessa cosa, a parità di codice, con condizioni di ottimizzazione diverse.
Intanto ho fatto un primissimo test al volo e mi risulta che utilizzando la const, al posto della define, servono 187.5 ns in più, ovvero tre cicli macchina.
Più tardi, con calma, faccio un test più approfondito e posto i risultati completi delle misure strumentali.

astrobeed:
Intanto ho fatto un primissimo test al volo e mi risulta che utilizzando la const, al posto della define, servono 187.5 ns in più, ovvero tre cicli macchina.
Più tardi, con calma, faccio un test più approfondito e posto i risultati completi delle misure strumentali.

Scusami, astro. Hai verificato se i 2 file binari sono diversi?

leo72:
Scusami, astro. Hai verificato se i 2 file binari sono diversi?

Il file è uno solo perché ho inserito nello stesso sketch tutti e due i test in modo da rilevare i tempi delle due condizioni con una singola misura in cascata.
Però prima di esprimermi in modo definitivo voglio approfondire meglio le cose.

magari se postassi anche l'assembly sarei curioso.
...se non ti risulta pesante...ho l'impressione di sembrare un po' un'invasato con questo asm. :blush:

qsecofr:
magari se postassi anche l'assembly sarei curioso.

Posterò lo sketch usato e gli screen capture delle misure strumentali, se vuoi vedere l'assembly basta che compili e dai il file .elf in pasto a AvrStudio :slight_smile: