Domanda

Sto programmando in C per il microcontrollore incorporato limitato alla RAM con RTOS.

Rompo regolarmente il mio codice in funzioni brevi, ma ogni chiamata di funzione richiede più memoria stack. Ogni compito ha bisogno del suo stack, e questo è uno dei principali consumatori di memoria del progetto.

Esiste un'alternativa per mantenere il codice ben organizzato e leggibile, conservando comunque la memoria?

È stato utile?

Soluzione

Prova a rendere lo stack di chiamate più piatto, quindi invece di a () chiamando b () che chiama c () che chiama < code> d () , hanno a () call b () , c () e d ( ) stesso.

Se una funzione viene referenziata una sola volta, contrassegnala inline (supponendo che il tuo compilatore lo supporti).

Altri suggerimenti

Esistono 3 componenti per l'utilizzo dello stack:

  • Indirizzi di ritorno chiamate funzione
  • Parametri di chiamata delle funzioni
  • variabili automatiche (locali)

La chiave per ridurre al minimo l'utilizzo dello stack è minimizzare il passaggio dei parametri e le variabili automatiche. Il consumo di spazio della chiamata alla funzione effettiva è piuttosto minimo.

Parametri

Un modo per affrontare il problema dei parametri è passare una struttura (tramite puntatore) anziché un gran numero di parametri.


foo(int a, int b, int c, int d)
{
...
   bar(int a, int b);
}

fai questo invece:


struct my_params {
   int a;
   int b;
   int c;
   int d;
};
foo(struct my_params* p)
{
   ...
   bar(p);
};

Questa strategia è buona se passi molti parametri. Se i parametri sono tutti diversi, potrebbe non funzionare bene per te. Si finirebbe con una grande struttura che viene passata in giro che contiene molti parametri diversi.

Variabili automatiche (locali)

Questo tende ad essere il più grande consumatore di spazio nello stack.

  • Le matrici sono il killer. Non definire array nelle funzioni locali!
  • Riduci al minimo il numero di variabili locali.
  • Utilizza il tipo più piccolo necessario.
  • Se il rientro non è un problema, è possibile utilizzare le variabili statiche del modulo.

Tieni presente che se stai semplicemente spostando tutte le variabili locali dall'ambito locale all'ambito del modulo, NON hai risparmiato spazio. Hai scambiato lo spazio dello stack con lo spazio del segmento di dati.

Alcuni RTOS supportano l'archiviazione locale dei thread, che alloca " globale " archiviazione per thread. Ciò potrebbe consentire di disporre di più variabili globali indipendenti su una base per attività, ma ciò renderà il codice non così semplice.

Nel caso in cui sia possibile risparmiare molta memoria principale ma avere solo un piccolo frammento di stack, suggerisco di valutare le allocazioni statiche.

In C, tutte le variabili dichiarate all'interno di una funzione sono " gestite automaticamente " il che significa che sono allocati in pila.

Qualificare le dichiarazioni come "statiche" li memorizza nella memoria principale anziché nello stack. Fondamentalmente si comportano come variabili globali ma ti consentono comunque di evitare le cattive abitudini che derivano dall'uso eccessivo di globuli. Puoi fare un buon caso per dichiarare buffer / variabili di grandi dimensioni e di lunga durata come statici per ridurre la pressione sullo stack.

Attenzione che questo non funziona bene / per niente se la tua applicazione è multithread o se usi la ricorsione.

Attiva l'ottimizzazione, l'allineamento specificamente aggressivo. Il compilatore dovrebbe essere in grado di incorporare metodi per ridurre al minimo le chiamate. A seconda del compilatore e degli switch di ottimizzazione che utilizzi, può essere utile contrassegnare alcuni metodi come inline (o può essere ignorato).

Con GCC, prova ad aggiungere le " -finline-funzioni " (o -O3) e possibilmente il " -finline-limit = n " bandiera.

Un trucco che ho letto da qualche parte in ordine per valutare i requisiti dello stack del codice in una configurazione integrata è riempire lo spazio dello stack all'inizio con un modello noto (DEAD in hex è il mio preferito) e lasciare che il sistema funzioni per un po '.

Dopo una normale corsa, leggere lo spazio dello stack e vedere quanto spazio dello stack non è stato sostituito durante il corso dell'operazione. Progettare in modo da lasciare almeno il 150% di quello in modo da affrontare tutti i percorsi di codice oscuri che potrebbero non essere stati esercitati.

Puoi sostituire alcune delle variabili locali con globali? Le matrici in particolare possono consumare stack.

Se la situazione ti consente di condividere alcuni globali tra alcuni quelli tra funzioni, c'è la possibilità che tu possa ridurre la tua impronta di memoria.

Il costo di scambio è una maggiore complessità e un rischio maggiore di effetti collaterali indesiderati tra le funzioni rispetto a un footprint di memoria possibilmente più piccolo.

Che tipo di variabili hai nelle tue funzioni? Di quali dimensioni e limiti stiamo parlando?

A seconda del compilatore e di quanto siano aggressive le opzioni di ottimizzazione, avrai un utilizzo dello stack per ogni chiamata di funzione effettuata. Quindi, per cominciare, probabilmente dovrai limitare la profondità delle chiamate di funzione. Alcuni compilatori usano i salti anziché i rami per funzioni semplici che ridurranno l'utilizzo dello stack. Ovviamente puoi fare la stessa cosa usando, diciamo, una macro assembler per saltare alle tue funzioni piuttosto che una chiamata di funzione diretta.

Come menzionato in altre risposte, l'integrazione è un'opzione disponibile anche se ciò comporta un costo di dimensioni maggiori del codice.

L'altra area che mangia stack sono i parametri locali. In quest'area hai un certo controllo. L'uso della statica (a livello di file) eviterà l'allocazione dello stack a costo dell'allocazione di RAM statica. Allo stesso modo i globali.

In casi (veramente) estremi puoi trovare una convenzione per funzioni che usano un numero fisso di variabili globali come memoria temporanea al posto dei locali nello stack. La cosa difficile è assicurarsi che nessuna delle funzioni che usano gli stessi globi venga mai chiamata contemporaneamente. (da qui la convenzione)

Se devi iniziare a preservare lo spazio dello stack dovresti ottenere un compilatore migliore o più memoria.

Il tuo software in genere crescerà (nuove funzionalità, ...), quindi se devi avviare un progetto pensando a come preservare lo spazio dello stack, è condannato dall'inizio.

Sì, un RTOS può davvero consumare RAM per l'utilizzo dello stack di attività. La mia esperienza è che come nuovo utente di un RTOS, c'è la tendenza a usare più attività del necessario.

Per un sistema incorporato che utilizza un RTOS, la RAM può essere un bene prezioso. Per preservare la RAM, per funzionalità semplici può essere comunque efficace implementare diverse funzionalità in un'unica attività, eseguendo il round robin, con un design multitasking cooperativo. Riducendo così il numero totale di compiti.

Penso che potresti immaginare un problema che non esiste qui. La maggior parte dei compilatori in realtà non fa nulla quando " alloca " variabili automatiche nello stack.

Lo stack viene allocato prima di " main () " viene eseguito. Quando si chiama la funzione b () dalla funzione a (), l'indirizzo dell'area di memoria immediatamente dopo l'ultima variabile utilizzata da a viene passato a b (). Questo diventa l'inizio dello stack di b () se b () quindi chiama la funzione c () quindi lo stack di c inizia dopo l'ultima variabile automatica definita da b ().

Si noti che la memoria dello stack è già presente e allocata, che non ha luogo l'inizializzazione e l'unica elaborazione coinvolta passa un puntatore allo stack.

L'unica volta in cui questo diventa un problema sarebbe quando tutte e tre le funzioni utilizzano grandi quantità di memoria, lo stack deve quindi adattarsi alla memoria di tutte e tre le funzioni. Cerca di mantenere le funzioni che allocano grandi quantità di memoria nella parte inferiore dello stack di chiamate, cioè non chiamare un'altra funzione da esse.

Un altro trucco per i sistemi con memoria è quello di dividere le parti di memoria di una funzione in funzioni autonome separate.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top