Go Down

Topic: Liberare Ram + F() (Read 2483 times) previous topic - next topic

testato

Due chiarimenti:

1) ho usato Serial.print(F()) e lcd.print(F());
i valori flash-ram prima e dopo sono questi (parlo dei valori mostrati durante la compilazione dall'ide 1.5)
Quote
(flash 15340 - ram 848) --> (15350 - 738)

mi chiedo, visto che risparmio 110byte sulla Ram, ma la flash si ingrandisce solo di 10byte, dove sono andati a finire gli altri 100byte ?

2) Se ho una stringa in Ram, mettiamo di 200byte, e poi ad un certo punto la svuoto cosi'
Code: [Select]
stringa = "";
I 200byte vengono liberati ? sono immediatamente disponibili al programma per essere ririempiti da altri 200byte ? Chiedo conferma perche' ho letto a volte di dover fare free() per liberare Ram

Grazie come sempre,
- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

leo72

1) attento, confondi il modo in cui i dati sono salvati e recuperati.
Siccome i micro sono basati su architettura Harvard, la memoria dei programmi è separata da quella dei dati. Una stringa viene salvata sempre in flash e poi, durante l'esecuzione, il codice la copia in Ram da cui poi il programma può prelevarla per usarla. Usando la funzione F() si accede a PROGMEM, una macro che istruisce il compilatore ad aggiungere al codice la funzione per poter leggere direttamente la stringa dalla Flash (questa è una capacità dei micro, ossia Read-While-Run, vale a dire possono leggere dalla Flash mentre è in esecuzione del codice).
Quindi non è che la stringa viene trasportata dalla Ram alla Flash (avresti ragione tu a cercare i 100 byte mancanti), ma semplicemente vengono aggiunti dei "pezzi di codice" (quei 10 byte in più) che servono al micro per poter prelevare la stringa, che è sempre memorizzata in Flash.

2) L'oggetto stringa è MOLTO ROGNOSO.
Si basa sull'allocazione dinamica della memoria, una cosa che in C++ funziona bene su sistemi PC dove c'è un garbage collector che si occupa di riciclare la memoria non più in uso. Sulle MCU il compilatore non implemente il garbage collector per cui l'assegnazione e lo svuotamento delle stringhe possono portare a frammentazione della memoria, con blocchi e reset.
Faccio un esempio.
Tu dichiari una stringa di 200 caratteri ad inizio programma, poi la svuoti e la riempi con 100 caratteri. Siccome hai già creato un "buco" di 200 byte, i 100 caratteri che ci metti in un secondo tempo stanno tutti in quel buco e non succedono problemi.
Se invece crei una stringa di 100 caratteri, poi la svuoti e la riempi con 200 caratteri, i 100 byte allocati in precedenza non bastano più per contenere la stringa. Il compilatore allora semplicemente alloca un nuovo blocco di 200 byte, lasciando un buco di 100 byte dietro di sé.... Fallo tante volte e capisci come un blocco software arriva in pochi passaggi (il famoso problema delle stringhe).

testato

#2
Nov 07, 2013, 10:41 pm Last Edit: Nov 07, 2013, 10:46 pm by Testato Reason: 1
1) credo fermamente che il tuo mestiere dovrebbe essere quello del Professore, le caratteristiche ci sono tutte, la competenza che non si discute ma che da sola non basta, la capacita' di adattarsi alle competenze altrui dando diversi livelli di risposta in base allo studente, infine la chiarezza. Non a caso, almeno dalle mie parti, solo ad alcuni, pochi purtroppo, viene ancora dato del "Chiarissimo Professore"  :)
Quindi ritornando alla mia domanda i byte sono giustamente gia' presenti nel calcolo della flash occupata, perche' fisicamente li' vanno a finire, in piu' il compilatore estrapola dal totale quelli che, analizzando il sorgente, gia' sa che vanno a finire in Ram. Quanta poi Ram verra' occupata a runtime non potra' mai dirlo nessun compilatore, ma e' comunque una info importante questa dell'IDE 1.5. Volendo sapere la ram libera durante il funzionamento lo si potra' fare solo dall'internmo del programma stesso. Se ho capito bene quindi discorso chiuso

2) sulla seconda questione le cose sono piu' rognose, permettimi di continuare con le domande:
2a) se memorizzo in una stringa 100B e poi li libero con "", poi dopo memorizzo nella stessa stringa 200B, in quel momento quindi mi restano occupati 300B ?
2b) se continuo e quindi libero dinuovo la stringa con "" e poi memorizzo 300B, questi vanno ad occupare i 300B ? cioe' i 200 liberati piu' i 100 precedenti ? (parlo sempre della stessa stringa con lo stesso nome)
2c) il comando reserve (stringa.reserve(200) aiuta molto ? visto che cmq all'invio della prima stringa da 200B cmq si crea lo spazio necessario automaticamente per contenerla, perche' usare il reserve ?

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

leo72

1) non voglio rubare il mestiere a Michele  ;)
Comunque direi che ci siamo. avr-size, il programma che viene usato per il calcolo dell'impegno delle memorie, analizza il codice e fa una previsione sul consumo delle variabili statiche, ossia quelle inizializzate con il classico byte a = 1 ad esempio. Qui il compilatore sa per certo che il consumo è 1 byte. Poi nella RAM ci vanno anche stack (per i salti, ossia le chiamate alle routine) ed heap (la memoria allocata dinamicamente, come appunto gli oggetti String). E queste sono cose che non sono prevedibili a priori.

2.a)
eh, sì. Perché hai 100 byte lasciati indietro dal compilatore.
Ovviamente se nel mezzo ci sono altre variabili.
Ossia mettiamo che la tua memoria sia:
Stringa di 100
byte
byte

Hai 2 byte occupati dopo la stringa. Se la "svuoti" e poi la riassegni con 200 caratteri, nel buco di 100 byte iniziale non ci sta più, quindi il compilatore va dopo i 2 byte e lì ricrea una stringa di 200 caratteri.
2.b) No. Il compilatore riusa i 100 byte iniziali solo se dentro può metterci un oggetto che è 100 byte oppure meno. Non esiste il concetto di divisione dei dati. Se la stringa è lunga 300 byte, questi devono essere tutti contigui l'uno all'altro.
2.c) con reserve hai la certezza di riservare quel quantitativo di memoria anche se la tua stringa è lunga, in fase di inizializzazione, di meno. In questo modo sai per certo che fino a che la stringa sarà lunga 200 o meno byte, essa starà sempre nello stesso spazio di memoria.

testato

ci siamo quasi  :smiley-sweat:

1) perche' parli di compilatore quando si parla di riallocare spazio di memoria ? cioe' a micro avviato, io mando una stringa al micro ed e' "il compilatore" che alloca lo spazio di ram necessaria ? non e' lo stack ? c'e' il compilatore anche nel firmware del micro ?

2)  quindi man mano che invio stringhe al micro queste vengono spezzettate in vari blocchi di ram, se inizialmente mando una stringa grande "il massimo possibile" e poi solo e soltanto stringhe piu' piccole, sarei rpotetto dalla creazione di questi blocchi piu' piccoli ? perche' mi sembra di capire che al contrario se io mando prima una stringa da 1char, poi da 2, poi da3, ecc ecc e' il modo peggiore per gestire la gia' infima ram ?

3) mettiamo che la stringa piu' grande da gestire e' 200B, creare il reserve200, oppure inizializzare la variabile stringa=200volteX e' equivalente ? visto che appunto spedendo poi al micro stringhe <=200B andranno sempre a finire nello stesso spazio ram, con entrambi i metodi reserve o preassegnazione di una variabile da 200 caratteri ?

4) la funzione free() cosa fa ? non c'e' u comando che riunisce questi spazi vuoti ma separati fra loro a causa di invio stringhe corte seguite da strinhge piu' lunghe ?
- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

uwefed

#5
Nov 08, 2013, 12:48 am Last Edit: Nov 08, 2013, 12:53 am by uwefed Reason: 1
1) Lo Stack é solo una parte della memoria dove vengono memorizzato temporaneamente date per essere poi di nuovo letti. I datti vengolo sempre letti nella sequenza inversa in cui sono stati memorizzati; viene letto sempre l' ultimo memorizzato poi il penultimo ecc.
Per esempio a livello linguaggio macchina  l' indirizzo di ritorno in un salto a un subroutine viene memorizzato nello stack  oppure nel caso si interrupt l'indirizzo da dove é stato interrotto il programma e i valore dei registri del CPU ecc.
Lo stack non ha intelligenza. é solo una parte della memoria RAM.

Non c'é fimware nello controllore e non c'e compilatore. Il compilatore traduce lo Sketch e poi viene inviata il codice macchina (file .hex) al Controller. Il controller genera il linguaggio macchina in modo che le cose funzionano. Percui genera il codice che ocupa i 100 byte di RAM e poi quello che li liberano e poi quello che occupa i 200 byte ecc.

2) Le stringhe non vengono spezzate. Vengono memorizzate nella RAM disponibile. Se nella memoria sono 100 byte poi 2 byte e poi 30 byte occupate cancellando la prima stringa si liberano i 100 byte ma gli altri byte non vengono spostati e non vengono neanche cancellati i caratteri della strinaga cancellata ma solo segnalati come non usati.


Ciao Uwe

leo72

1) è il compilatore che si occupa di tradurre il sorgente in codice eseguibile, è lui che genera il codice per i meccanismi che ti ho descritto.

2) sì, è così.

3) reserve(200) è più comodo rispetto a fare stringa="...........200 caratteri!!!....."

4) libera la memoria di un oggetto allocato dinamicamente. Il problema è che siccome non hai i meccanismi di gestione della garbage di un linguaggio come il C++, free è fatto a livello software e lascia buchi nella RAM se se ne abusa.

Maurotec

Tanto per dire qualcosa anche io, magari serve come spunto per approfondimenti.

Il C/C++ non ha una garbage collect, cioè non ci sono meccanismi insiti nel compilatore per gestire la garbage, anche perchè la garbage è tale solo se è dinamica per cui per gestire la garbage ci vuole codice che ha questa funzione. Il C++ è predisposto per fare cose che lo rendono totalmente diverso da quello che fa normalmente,
e anche per questo esiste una librerie garbage collect che non ho mai usato. Sul PC c'è un kernel che gira e che gestisce tutto le risorse e fa tante cose belle, come ad esempio se faccio la pipi fuori dal vasino mi termina come farebbe terminator, per di più io non riesco a fare la pipi fuori da vasino non perchè il kernel mi uccide brutalmente, ma perchè a me il kernel mi ha concesso un processo e mi ha riservato uno spazio di memoria su cui lavorare, il tentativo di scrivere fuori dallo spazio concessomi non va a buon fine a priori, quindi il kernel mi uccide solo per precauzione. Insomma e come se si accorgesse che premedito di fare la pipi fuori dal vasino.

Se io non faccio pulizia si verifica il memory leack, in sostanza richiedo sempre più memoria al kernel che arriva anche a fornirmi memoria non ram, cioè virtual memory. In C++ come anche in C liberare la memoria è un pensiero che deve venire prima di occuparla, il codice per liberare la memoria lo scrivi prima di malloc o new ecc.

Quindi sul PC abbiamo il kernel che ci termina se miniamo la sopravvivenza del sistema, sul microcontroller non abbiamo nessuno che pensa alla sopravvivenza del sistema semplicemente perchè non esiste un sistema.

Anche con gli RTOS, non puoi pensare di avere servizi simili a quelli di un kernel linux o altro kernel che gira su hardware con grandi risorse. Se non si è capito il problema è proprio, "le ridotte risorse".

Con linux un processo equivale a: figlio mio tieni questo è il tuo spazio per giocare, se te ne serve di più chiedimelo, se non me lo chiedi e tenti di uscire da qui ti uccido.

Ciao.

Go Up