Go Down

Topic: risparmiare ram (Read 2246 times) previous topic - next topic

astrobeed


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.





qsecofr

questo codice...

Code: [Select]
#define BOO 3

const byte bii = 2;

byte res;
byte ciccio;
void setup() {

ciccio = BOO;
res = bii;
}

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

}


viene tradotto così:
Code: [Select]
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 <loop> dove l'unica cosa curiosa è che per incrementare di 1 un byte lui toglie 255  :smiley-eek: (...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.


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.

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

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

astrobeed



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.

astrobeed


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.

leo72


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.


Risultato di diff eseguito su di essi:
Code: [Select]

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


ƎR

usando define però non posso usare PROGMEM, no?
Riccardo Ertolupi of the Vicenza Thunders Team: http://www.VicenzaThunders.com

leo72


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

#define PROGMEM   __ATTR_PROGMEM__

che può essere poi riutilizzato:
Code: [Select]
#define PSTR ( s )   ((const PROGMEM char *)(s))


astrobeed


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:

Code: [Select]

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


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;

astrobeed


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.

leo72


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?

astrobeed


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.

qsecofr

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

astrobeed


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

Go Up