Go Down

Topic: Memoria e dintorni... (Read 182 times) previous topic - next topic

Federico66

Jan 23, 2020, 08:18 pm Last Edit: Jan 23, 2020, 09:25 pm by Federico66
Premessa: creo questo nuovo thread, perchè non direttamente collegato al WordClock e all'analizzatore di spettro, perchè singolarmente funzionano benissimo.
Arduino Nano clone.

Mi succede una cosa strana che non riesco a spiegarmi, anche se il risultato sembra chiaro!

Alla compilazione del programma del WordClock (uso abbondantemente PROGMEM con array di uint16_t) ottengo:

Code: [Select]

Lo sketch usa 20518 byte (66%) dello spazio disponibile per i programmi. Il massimo è 30720 byte.
Le variabili globali usano 836 byte (40%) di memoria dinamica, lasciando altri 1212 byte liberi per le variabili locali. Il massimo è 2048 byte.

All'avvio => freeMemory = 295 bytes


per giocherellare decido di inserirci l'analizzatore, quindi aggiungo la libreria "arduinoFFT", compilo
... risultato, i led della strip non si accendono!

Ho controllato con il multimetro, sul pin con c'è tensione!

Mi viene subito un dubbio, inserisco la freeMemory e ricompilo, ottengo questo!!!

Code: [Select]

Lo sketch usa 20584 byte (67%) dello spazio disponibile per i programmi. Il massimo è 30720 byte.
Le variabili globali usano 1103 byte (53%) di memoria dinamica, lasciando altri 945 byte liberi per le variabili locali. Il massimo è 2048 byte.

All'avvio => freeMemory = 897 bytes


La memoria libera sembra essere aumentata!!!

Dopo vari tentativi per capire, scopro che effettivamente all'inizializzazione della classe

Code: [Select]

double data_real[64];
double data_img[64];
arduinoFFT FFT = arduinoFFT(data_real, data_img, SAMPLES, SAMPLING_FREQUENCY);

//cosi definita

//arduinoFFT::arduinoFFT(double *vReal, double *vImag, uint16_t samples, double samplingFrequency) {
// this->_vReal = vReal;
// this->_vImag = vImag;
// this->_samples = samples;
// this->_samplingFrequency = samplingFrequency;
// this->_power = Exponent(samples);
//}


la memoria va in pappa, almeno credo, visto il risultato!!

Chiaramente non mi interessa risolvere per avere il giochino con microfono e led, ma mi interessa capire come faccio a conoscere (quando si usa PROGMEM, perchè immagino che il problema sta tutto li) la reale situazione delle memorie?
Perchè la freeMemory torna un valore più alto?


Spero di essere stato chiaro, altrimenti bacchettatemi ;)

TIA
Federico

PS
Se volete allego le 1500 righe del programma, ma non credo sia utile.
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Maurotec

Bel quesito, purtroppo non ho risposte precise. Dietro sembra esserci l'arcano, cioè l'ide dice essere capace di stabilire quanta memoria dinamica viene usata, ma è davvero capace di calcolare ciò? Mi pare impossibile poiché dinamicamente questa muta a run-time mentre il calcolo viene fatto da avr-size sul file binario.

Poi c'è da vedere bene cosa fa freeMemory, sicuramente fa qualcosa a runtime, ma ho come l'impressione che menta non sapendo di mentire quando si è allocata più memoria di quella disponibile.

Riguardo a progmem tutto viene deciso a tempo di compilazione, quindi staticamente, poi a run-time per leggere dalla flash serviranno puntatori in ram, buffer ecc e li dipende da come si decide di operare. Solitamente si sfrutta un blocco di codice (es una funzione) all'interno un buffer, si trasferisce da flash al buffer si fa quel che serve e al termine della funzione il buffer cessa di esistere (si uso lo stack).

Progmem non fa altro che dire di non allocare memoria statica per quelle variabili const, cosa che normalmente viene fatta per inizializzare le variabili a run-time, cioè nel compilato viene introdotto del codice che copia dalla flash alla ram per avere appunto la ram inizializzata. Per quelle variabili in cui è presente l'attributo progmem questa copia implicita da flash a ram non avviene, poiché avverrà su richiesta a run-time.

Code: [Select]
double data_real[64];
double data_img[64];

Quindi al wordclock ci aggiungi la FFT, giusto?
Già bastano questi due buffer aggiuntivi per dire che la ram libera non può certo aumentare ma deve diminuire.

PS:visto l'analizzatore FFT forte, ma forse è lento oppure il video non è in sincro con l'audio. +1

Ciao.

Federico66

Bel quesito, purtroppo non ho risposte precise. Dietro sembra esserci l'arcano ...
Più o meno è il ragionamento che ho fatto io...
Su freeMemory, si non so quanto sia affidabile, ne ho provati vari e ognuno da risultati diversi :)


Code: [Select]
double data_real[64];
double data_img[64];

 
Quindi al wordclock ci aggiungi la FFT, giusto?
Già bastano questi due buffer aggiuntivi per dire che la ram libera non può certo aumentare ma deve diminuire.
Esatto, ma il problema (strip led), nasce solo quando viene istanziata la classe

Code: [Select]

arduinoFFT FFT = arduinoFFT(data_real, data_img, SAMPLES, SAMPLING_FREQUENCY);


Infatti se commento la riga, e le relative chiamate (lasciando tutte le variabili), l'orologio torna a funzionare!

Appena ho un po' di tempo, provo ad eliminare altre funzionalità del programma per verificare cosa succede.

Per quando riguarda il debug della memoria, ho trovato questo, ma non riesco a farlo funzionare! In realtà ho fatto pochi tentativi :(


Grazie
Federico


[OT]

PS:visto l'analizzatore FFT forte, ma forse è lento oppure il video non è in sincro con l'audio. +1
Grazie, naturalmente non è tutta roba mia, ma mi sono divertito molto, e ho potuto testare il mic, che funziona veramente bene. Riesce a percepire qualunque rumore nella stanza (tastiera) e anche quelli che arrivano dalla strada!!

In giornata, se riesco, metto su l'ultima versione del programma, mi sembra abbastanza veloce, in ogni caso, si può agire sulla frequenza di campionamento e quindi sul periodo.



"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

nid69ita

#3
Jan 24, 2020, 11:18 am Last Edit: Jan 24, 2020, 11:19 am by nid69ita
La memoria libera è difficile da calcolare a causa dell'allocazione dinamica, che sia in heap o in stack.
Anche perché cambia in base al punto del programma in cui sei e cambia anche nel tempo, sempre a causa di eventuale allocazione dinamica; ad esempio sappiamo che String alloca dinamicamente e un programma che le usa potrebbe partire con free di 1000 byte ma poi durante 2h la memoria si frammenta e non hai più quei byte liberi.
Uno sketch Arduino come quello che hai postato, usa librerie con oggetti/classi, che allocano dinamicamente, difficile dire cosa succede nel tempo e molte volte non sappiamo neppure cosa fa la libreria internamente.
my name is IGOR, not AIGOR

Federico66

La memoria libera è difficile da calcolare ...
Di questo sono conscio, ma io non mi spiego come la sola inizializzazione della classe
Code: [Select]

arduinoFFT FFT = arduinoFFT(data_real, data_img, SAMPLES, SAMPLING_FREQUENCY);

sballi tutto.

Dopo tutto, quella chiamata non fa altro che inizializzare le variabili

Code: [Select]

arduinoFFT::arduinoFFT(double *vReal, double *vImag, uint16_t samples, double samplingFrequency)
{
  this->_vReal = vReal;
  this->_vImag = vImag;
  this->_samples = samples;
  this->_samplingFrequency = samplingFrequency;
  this->_power = Exponent(samples);
}


cosi definite

Code: [Select]

private:
  /* Variables */
  uint16_t _samples;
  double _samplingFrequency;
  double *_vReal;
  double *_vImag;
  uint8_t _power;

Code: [Select]

uint8_t arduinoFFT::Exponent(uint16_t value)
{
  // Calculates the base 2 logarithm of a value
  uint8_t result = 0;
  while (((value >> result) & 1) != 1) result++;
  return(result);
}


la situazione iniziale è questa, e mi pare decente come gestione della memoria

Code: [Select]

Lo sketch usa 20446 byte (66%) dello spazio disponibile per i programmi. Il massimo è 30720 byte.
Le variabili globali usano 836 byte (40%) di memoria dinamica, lasciando altri 1212 byte liberi per le variabili locali. Il massimo è 2048 byte.


Comunque, per capire meglio cosa succede (la mia è solo curiosità), con calma devo "smantellare" il programma un pezzo alla volta... d'altronde, come sempre ;)

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

nid69ita

#5
Jan 24, 2020, 02:17 pm Last Edit: Jan 24, 2020, 02:24 pm by nid69ita
Ovviamente sono d'accordo con te che aggiungendo una lib la freeMemory() dovrebbe dare meno spazio.
Tu richiami la freeMemory() ad inizio, immagino nella setup()
Io sinceramente NON ricordo in un programma C++ quando un oggetto dichiarato tra le globali viene allocato e quando ne viene richiamato il costruttore. In teoria prima della setup() visto che il main() di uno sketch Arduino è di nascosto circa:
Code: [Select]
void main()
{ setup();
  while(1) loop();
}


EDIT: Leggo su vari siti, che in teoria l'allocazione di un oggetto globale e quindi la chiamata al suo costruttore, NON è garantita sia fatta prima del main() e quindi prima della tua freeMemory()
ma è garantito sia fatto prima che tu usi quel oggetto. E dipende da come è implementata la cosa nel compilatore che usi:
"It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized"
my name is IGOR, not AIGOR

Federico66

... in teoria l'allocazione di un oggetto globale e quindi la chiamata al suo costruttore, NON è garantita sia fatta prima del main() e quindi prima della tua freeMemory()
ma è garantito sia fatto prima che tu usi quel oggetto.
Chiarissimo grazie.

Nel frattempo ho fatto qualche prova con memdebug consigliata da Gammon, e riletto tutto il suo articolo in merito a Progmem.
In effetti, come ipotizzato più volte e da molti di voi, mi pare di capire che non sia facile determinarne l'uso della memoria in fase di compilazione e quindi bisogna sempre valutarne gli effetti a runtime, possibili crash (appunto)!

Per quanto mi riguarda, il problema è chiaro, e l'unica soluzione è... non chiedere troppo al piccolo Arduino :)

Grazie per la solita disponibilità
Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

nid69ita

In un thread recente chiedono dell'uso di una lib per mandare dati a database InfluxDB
Libreria per ESP8266/ESP32.  Che dentro richiama #include <list>       !!
ovvero libreria STL che c'e' nei core ESP e nel core per MRK (scoperto oggi!) , schede con molta memoria.
Esiste una lib STL x AVR (arduino uno/mega/etc)  ma si legge chiaramente, usa pesantemente allocazione dinamica, ergo  okay su schede con molta RAM, ma su Arduino Uno/Mega è farsi del male ^-^
my name is IGOR, not AIGOR

Federico66

Sviluppo software da 30+ anni, ma mi ero dimenticato di cosa significasse stare attenti all'uso della memoria!
Devo ringraziare il C e Arduino, se adesso anche quando dichiaro una variabile (in Net) penso due volte prima di definire il tipo :D :D

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

nid69ita

Manda una email come promemoria di questa cosa a quelli di Microsoft, che ogni volta scrivono un nuovo sistema operativo, richiede sempre più ram  ;D ;D ;D
my name is IGOR, not AIGOR

Maurotec

#10
Jan 24, 2020, 07:16 pm Last Edit: Jan 24, 2020, 07:24 pm by Maurotec
Quote
Infatti se commento la riga, e le relative chiamate (lasciando tutte le variabili), l'orologio torna a funzionare!
Occhio a gc-section del linker, solo ciò che è referenziato viene allocato, tradotto il altri termini se leggi o scrivi in quei buffer li stai usando ed è naturale che siano allocati staticamente, mentre se non li usi il linker non prevede spazio per loro e a run-time non esistono proprio.

Quote
Riesce a percepire qualunque rumore nella stanza (tastiera) e anche quelli che arrivano dalla strada!!
Si immaginavo, merito o colpa del compressore della dinamica che aumenta o riduce il guadagno closed-loop dell'amplificatore in base al segnale di uscita. Per cui rumori vicini al microfono vengono compressi e rumori lontani vengono espansi.

Quote
se adesso anche quando dichiaro una variabile (in Net) penso due volte prima di definire il tipo :D :D
Siamo su un altro pianeta in un'altra galassia, cioè c'è la virtual machine, c'è il sistema operativo, c'è il pc e in mezzo tante altre cose. Tra il 328 e il codice che scrivi non c'è di mezzo altro.

Quote
n un thread recente chiedono dell'uso di una lib per mandare dati a database InfluxDB
Libreria per ESP8266/ESP32.  Che dentro richiama #include <list>       !!
ovvero libreria STL che c'e' nei core ESP e nel core per MRK (scoperto oggi!) , schede con molta memoria.
L'ho fatto anche io sul 328 una linked list dinamica, me lo sentivo che qualcosa prima o poi doveva andare a putt.. e infatti ci è andata. In sostanza se allochi dinamicamente solo all''inzio e non esegui mai free lavora come se allocassi staticamente, il problema è nella riallocazione di memoria, prima o poi (questione di tempo) sarà tanto frammentata da essere inutilizzabile.

Ciao.

Go Up