[RISOLTO] Memoria:allocazione e de-allocazione

Ciao,
forse mi sto perdendo in un bicchiere d'acqua, ma ho un dubbio sulla gestione della memoria, e non so come verificarlo in Arduino!
Le seguenti soluzioni, sono equivalenti in merito alla de-allocazione della memoria?
Nel secondo caso, arrLoc, viene liberata normalmente come tutte le variabili locali? Vero?
Ho visto usare la terza soluzione, ma non mi è chiara l'utilità rispetto alle altre!

In questi mesi, al bisogno, ho usato la prima soluzione, ma dopo aver visto la terza, la questione mi ha incuriosito.

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

  //1: passo array come riferimento dopo averla allocata
  uint8_t* arr1 = (uint8_t *)malloc(4);
  getArray1(arr1, 4);
  for(byte i = 0; i < 4; i++) {
    Serial.println(arr1[i]);
  }
  free(arr1);

  //2: creo array nella funzione
  uint8_t* arr2 = getArray2(4);
  for(byte i = 0; i < 4; i++) {
    Serial.println(arr2[i]);
  }
  free(arr2);

  //3:
  uint8_t* arr3;
  arr3 = getArray3(arr3, 4);
  for(byte i = 0; i < 4; i++) {
    Serial.println(arr3[i]);
  }
  free(arr3);
}

//1:
void getArray1(uint8_t* arr, uint8_t len) {
  //... riempio array
  arr[0] = 11;
  arr[1] = 12;
  arr[2] = 13;
  arr[3] = 14;
}

//2:
uint8_t* getArray2(uint8_t len) {
  //inizializzo array (memoria)
  uint8_t* arrLoc = (uint8_t *)malloc(len);
  //... riempio array
  arrLoc[0] = 21;
  arrLoc[1] = 22;
  arrLoc[2] = 23;
  arrLoc[3] = 24;  
  return (uint8_t*)arrLoc;
}

//3:!!!! perchè?
uint8_t* getArray3(uint8_t* arr, uint8_t len) {
  //inizializzo array (memoria)
  arr = (uint8_t *)malloc(len);
  //... riempio array
  arr[0] = 31;
  arr[1] = 32;
  arr[2] = 33;
  arr[3] = 34;  
  return (uint8_t*)arr;
}

TIA
Federico

Uhm, a me non è mai capitato di dover allocare in questo modo la memoria (su Arduino intendo, in C su Unix si...), quindi seguo, ma mi chiedevo una cosa, una mia curiosità: perché ti serve questa complicazione? Perché non usare array preallocati come globali o come locali ad una funzione o classe?

docdoc:
... ma mi chiedevo una cosa, una mia curiosità: perché ti serve questa complicazione? Perché non usare array preallocati come globali o come locali ad una funzione o classe?

Nel progetto attuale (wordclock) ho delle matrici di interi dichiarati PROGMEM, ma di volta in volta mi serve solo una parte degli elementi e per evitare di ciclare sempre con pgm_read_byte etc all'interno della funzione principale, ho creato delle funzioni specifiche alle quali passo la riga che mi serve e mi restituisce una matrice.
Non so se mi sono spiegato, faccio prima a postare un esempio :slight_smile:

//globale
const PROGMEM uint16_t hours[71] = {
  280, 281, 282, 283,                                     //0:LUNA
  204, 205, 206,                                          //1:DUE
  207, 208, 209,                                          //2:TRE
  //...
};
const PROGMEM uint8_t hours_idx[14] = { 0, 4, 7, 10, 17, 23, 26, 31, 35, 39, 44, 50, 61, 71 };


//funzione
uint16_t* getHourArray(uint8_t row, uint8_t* len) {
  *len = pgm_read_byte(&hours_idx[row + 1]) - pgm_read_byte(&hours_idx[row]);
  uint16_t* arr = (uint16_t *)malloc(*len);
  uint8_t idx = 0;
  for (uint8_t col = pgm_read_byte(&hours_idx[row]); col < pgm_read_byte(&hours_idx[row + 1]); col++) {
    arr[idx] = pgm_read_word(&hours[col]);
    idx++;
  }
  return (uint16_t*)arr;


//che richiamo cosi
uint8_t row = 0;
uint8_t len = 0;
uint16_t* arr;

row = 1;
arr = getHourArray(row, &len);
//lavoro con arr e len

Chiaro che potevo anche farne a meno, ma così lo trovo più pulito e leggibile :slight_smile:

Per tornare al post, siccome sto avendo degli strani problemi (che veramente non capisco), il primo sospetto è caduto sulla memoria, anche perchè avevo l'impressione che l'uso di PROGMEM falsificasse i valori di memoria libera restituiti in fase di compilazione, che sono buoni!

Lo sketch usa 17264 byte (56%) dello spazio disponibile...
Le variabili globali usano 1067 byte (52%) di memoria dinamica...

In realtà, nel frattempo sto utilizzando la libreria freeMemory (non so che attendibilità abbia), ma l'esito sembra buono; dopo 30 minuti 940 costante!
Continuerò ad indagare :slight_smile:

Resta valido l'OP, a questo punto, più per curiosità.

Federico

PS
Chiaramente potevo adottare delle matrici del tipo {first_led, len}, invece dell'elenco dei led, ma siccome ho parecchi casi di led non consecutivi, ho adottato lo stesso criterio per tutte le "parole".

Ma sono funzioni che ci sono su Arduino?

Ciao Uwe

uwefed:
Ma sono funzioni che ci sono su Arduino?

Scusa, ma non capisco la domanda!
Quali funzioni?
Sto solo allocando memoria per una matrice definita a puntatore.

Federico

secondo me usare l'allocazione dinamica su Arduino è semplicemente sbagliato

comunque secondo me la migliore è la prima,
la seconda è piu' snella, ma più pericolosa, potrebbe esserci qualche allocazione non liberabile tra la alloc e la free
la terza è simile, ma mantiene una sorta di continuità tra i (il, uno è) puntatori della malloc e della free
che nella seconda sono due (uguali ma due)

io però preferisco la quarta

voif foo()
{
   byte array[dimensione];
   foo1(array, dimensione);
}

sempre puntatore passa
ma uso un VLA invece che una malloc, ipoteticamente un miglior controllo dal parte del compilatore

so che Linus si è espresso contro l'adozione dei VLA in C, a suo dire ridondanti esistendo già la malloc

io sono troppo giovane, inesperto e dilettante per esprimermi

Io invece mi chiedo come funziona l'allocazione e de allocazione su un sistema senza sistema operativo?
Nel senso se io sono su un PC con sistema operativo,alloco memoria il sistema operativo utilizza quella che sa ram libera, de alloco tiene conto che quella memoria e utilizzabile.
Su un microcontrollore senza sistema operativo l'unico momento dove mi posso rendere conto della memoria è il momento della compilazione.
Per spiegarmi meglio, durante la compilazione alloco memoria, sempre durante la compilazione faccio un free() la memoria diventa libera e il compilatore può decidere di usarla per altri scopi, ma se allocazione e de allocazione dovesse avvenire dopo aver compilato, il sistema non può rendersi conto di quali parti di ram siano utilizzate o libere.

C1P8:
secondo me usare l'allocazione dinamica su Arduino è semplicemente sbagliato

Onestamente non avendo molta esperienza di micro, non saprei; in altro progetto su ESP32 (chiaramente con molta più ram!) l'ho usata tantissimo e non ho avuto alcun problema, ma se mi confermate che è "sbagliato", l'ho accetto senza riserve :slight_smile:

C1P8:
comunque secondo me la migliore è la prima,
la seconda è piu' snella, ma più pericolosa, potrebbe esserci qualche allocazione non liberabile ...

Concordo, ed è motivo del mio dubbio :slight_smile:

C1P8:
io però preferisco la quarta

voif foo()

{
  byte array[dimensione];
  foo1(array, dimensione);
}

Non capisco perchè sia "migliore", è pur sempre dinamica

Grazie
Federico

Federico66:
Onestamente non avendo molta esperienza di micro, non saprei ...

L'utilizzo di malloc() e free(), su piccole MCU, dove non c'è un sistema operativo che fa tutti i controlli, dove non c'è un garbage collection ed un compattatore di memoria, ma dove devi fare tutto tu ... porta quasi sempre a problemi di frammentazione della memoria, mancanza di zone libere di SRAM e crash di programma.

Diverso è il caso che tu citi delle variabili locali ... quelle sono gestite dal compilatore nello stack e NON con delle malloc() e free()!

Guglielmo

torn24:
Per spiegarmi meglio, durante la compilazione alloco memoria, sempre durante la compilazione faccio un free() la memoria diventa libera e il compilatore può decidere di usarla per altri scopi, ma se allocazione e de allocazione dovesse avvenire dopo aver compilato, il sistema non può rendersi conto di quali parti di ram siano utilizzate o libere.

Effettivamente.
Come avrai letto sto avendo problemi e quindi sto indagando. Il primo indiziato è proprio la memoria, ma prima di "sfasciare" il software vado per piccoli tentativi.

Federico

gpb01:
... porta quasi sempre a problemi di frammentazione della memoria, mancanza di zone libere di SRAM e crash di programma.

Chiarissimo come sempre, grazie.
Rivedrò il software così confermerò o meno se il problema era lì.

Grazie
Federico

Federico66:
Non so se mi sono spiegato, faccio prima a postare un esempio :slight_smile:

Grazie, si con l'esempio mi è più chiaro.

Pur precisando che ovviamente non conoscendo l'intero codice non so giudicare al meglio, io al posto tuo avrei allocato una sola volta all'inizio un'area di capienza sufficiente da usare come "buffer" per tutte le letture, evitando così di fare malloc e free in continuazione che, seppure io non li abbia mai usati su Arduino, come dicevo, ho forti dubbi circa la loro efficienza e sicurezza (come poi anche Guglielmo mi pare confermi).

PS ma la PROGMEM l'hai usata perché sei arrivato "a tappo" con la RAM o è per "scrupolo"/"sfizio"? :wink:

docdoc:
...come poi anche Guglielmo mi pare confermi.

Ho già risolto (mentre pranzavo :slight_smile: ), nel senso che ho eliminato le funzioni che creano matrici d'appoggio (dinamiche)... Ho creato una funzione alla quale passo il "tipo" (matrice che deve leggere) e quindi la cicla direttamente...

void openLedArray(enum ARRAY_TYPE type, uint8_t row) {
  switch (type) {
    case ARR_START: {
      //..omissis..
    }
    case ARR_HOUR: {
      uint8_t first = pgm_read_byte(&hours_idx[row]); 
      uint8_t last = pgm_read_byte(&hours_idx[row + 1]);  
      for (uint8_t col = first; col < last; col++) {
        openLed(pgm_read_word(&hours[col]));
      }       
      break;
    }
    case ARR_MIN: {
      //..omissis..
    }
  }
}

... ma il problema iniziale non sembra risolto!!!

Farò qualche altro test, altrimenti posto tutto il programma.

Nota: non è che non voglio postarlo (alla fine cmq lo condividerò), ma mi piacerebbe capire da solo dove sta il problema :slight_smile:

docdoc:
PS ma la PROGMEM l'hai usata perché sei arrivato "a tappo" con la RAM o è per "scrupolo"/"sfizio"? :wink:

Nessun tappo riusciva a chiudere :slight_smile:

Probabilmente ho fatto delle scelte iniziali errate, quindi ho ripiegato sulla PROGMEM.
Nulla mi vieta di riprogettare il tutto, ma prima mi piacerebbe capire perchè ho strani problemi con questa versione... e se non è la memoria, allora non mi rimane che la gestione della striscia led!

Grazie
Federico

PS
Se non ne vengo fuori, apro un altro thread.

[OT]

Mi viene un dubbio!
Forse le stranezze riscontrate dipendono dall'uso delle librerie AltSoftSerial e Adafruit_NeoPixel!!!

Cerco in rete...

[UPDATE]
Trovato effettivamente materiale che potrebbe spiegare le stranezze:
Link1
Link2
Link3

Federico

[OT]

Confermo che le stranezze riscontrate nel mio programma, sono dovute all'uso contemporaneo dei led WS2812B e della seriale (hardware/software, scrittura/lettura).

Se disabilito il debug (scrittura su seriale hardware) o disabilito la ricezione da bluetooth (seriale software), il led si accendono perfettamente e come da programma.

Oltre ai link del precedente post, a chi interessa, consiglio anche questo.

Quindi malloc e free sono stati accusati ingiustamente, ma ho imparato che se ne può fare a meno :smiley: :smiley: :smiley:

A parte le battute, appena riesco, proverò a capire se è possibile aggirare il problema, altrimenti, non userò i led durante le fasi di sviluppo, ma solo a software ultimato :confused: , sempre che il bt funzioni, altrimenti dovrò rivedere il progetto :slightly_frowning_face: :slightly_frowning_face:

Federico

Il problema è che nonostante io lo abbia scritto un'infinità di volte, tutti voi cercate la "pappa fatta" (le librerie di Adafruit) e vi ostinate ad utilizzare i WS2812 che danno un sacco di problemi ... ::slight_smile:

Siete mai andati a leggervi esattamente come funzionano e che tempistiche vogliono? Se lo aveste fatto avreste scoperto che sono una vera schifezza, con identificazione dei bit 1 e 0 in base alla periodo alto e basso del segnale (parliamo di centinaia di nanosecondi) e con uno streaming di dati non interrompibile ed a frequenza fissa ...


... la seriale usa gli interrupts e ... da qui un sacco di problemi!

Comprate gli APA102 ... banali, un pin dati, un pin clock, che può anche fermarsi o raggiungere svariati MHz (ci si riescono a fare schermi video di una certa dimensione, grazie alla velocità di aggiornamento) e tutti questi problemi di tempistiche ... come per incanto, svaniscono ... :smiley:

Guglielmo

gpb01:
Il problema è che nonostante io lo abbia scritto un'infinità di volte, tutti voi cercate la "pappa fatta" (le librerie di Adafruit) e vi ostinate ad utilizzare i WS2812 che danno un sacco di problemi ... ::slight_smile:

Hai ragione, ma, non avendo cercato, non ho letto nulla di tuo sull'argomento :frowning:

Nella rete, della quale oramai mi fido poco, tutti usano quei led e quelle librerie, probabilmente anche perché costano effettivamente poco.

Comunque, non ho dubbi che troverò una soluzione, in realtà già trovata, visto che il solo bt non provoca problemi in quanto l'invio dei dati verso i led, avviene sempre in momenti diversi dalla lettura della seriale bt; quindi devo continuare a non sovrapporre le due scritture/letture (led e seriale).

Ho avuto difficoltà perché, non avendo avuto da subito i led, ho fatto uso esagerato della seriale hardware per il debug, cosa che posso continuare a fare fino alla messa produzione del progetto.

Naturalmente, per eventuali atri progetti, valuterò a priori l'uso o meno di quei led. :slight_smile: :smiley:

Grazie
Federico

Federico66:
... probabilmente anche perché costano effettivamente poco.

Gli APA102, come prezzo, sono allineati ... su Aliexpress trovi strip di varia lunghezza (e vari indici IP) per tutti i gusti (esempio QUI) :wink:

Guglielmo

gpb01:
Gli APA102, come prezzo, sono allineati ... su Aliexpress trovi strip di varia lunghezza (e vari indici IP) per tutti i gusti (esempio QUI) :wink:

... e va beh... non li conoscevo, quindi non li ho cercati... "mò me lo segno..." :smiley: :smiley:

Federico

Federico66:
... e va beh... non li conoscevo, quindi non li ho cercati... "mò me lo segno..." :smiley: :smiley:

... ecco, mo li conosci, nun te sbaja' più ... :smiley: :smiley: :smiley: :smiley:

Guglielmo

P.S.: se un giorno li prendi ed hai bisogno di aiuto, fai un fischio che c'ho giocato parecchio :wink: