Normalmente, não tenho problemas para decidir se alguns dados devem ser globais, estáticos ou na pilha (sem alocação dinâmica aqui, portanto, sem uso da pilha). Também li algumas perguntas e respostas como essa, mas minha pergunta é mais específica, pois envolve uma enorme quantidade de dados, enorme em comparação com a memória do sistema.
Estou trabalhando com um código existente que tento melhorar (design, possíveis problemas, performances etc.). Esse código é executado em um antigo MCU de 8 bits com apenas 4KB de RAM . Neste código, enfrento o uso de uma matriz de quase 1 KB (sim, 1 KB em um sistema de RAM de 4KB ). Cada byte dessa matriz é usado, essa não é a questão. O problema é que essa matriz é uma matriz estática no arquivo em que foi declarada, portanto seu ciclo de vida é o mesmo do programa (ou seja, pode ser considerado infinito).
No entanto, depois de ler o código, acabei de descobrir que esse array não precisa de um ciclo de vida infinito; ele é construído e tratado de maneira totalmente processual; portanto, devemos poder declará-lo apenas na função em que é usado, dessa forma, ele estaria na pilha e, portanto, salvaríamos esse 1 KB de RAM.
Agora a pergunta: seria uma boa ideia? Do ponto de vista do design, se não precisar de um ciclo de vida infinito / global, ele pertence à pilha. Mas ei, isso é 1KB de 4KB, não há nenhuma desvantagem em alocar 25% da RAM assim? (que pode ser 50% ou mais da pilha)
Alguém poderia compartilhar alguma experiência com esse tipo de situação ou alguém pensa em algum motivo válido para não colocar essa matriz na pilha? Estou procurando desvantagens técnicas e comentários sobre o design.
A única coisa que tenho consciência é que tenho que ter certeza de que tenho 1 KB de pilha livre ao entrar nessa função. Talvez seja tudo o que tenho para resolver, talvez não.
Respostas:
Sim, e isso é uma forte restrição. É melhor ter certeza estaticamente do que ter um espaço tão grande disponível na pilha. Se o código for pequeno, se você estiver usando um GCC recente para compilar seu código, consulte isso .
BTW, alguns microprocessadores baratos podem usar um quadro de chamada "grande" mais caro que um "normal" (por exemplo, porque o conjunto de instruções favoreceria um deslocamento de um byte do ponteiro da pilha). YMMV.
Além disso, se você estiver codificando em C e se achar que seu grande array pode ter seu espaço reutilizado para outros fins, considere torná-lo um membro da união (com uma variável global de
union
tipo). Sim, isso é muito feio.Como alternativa, você pode considerar a codificação de um alocador de heap primitivo adequado ao seu aplicativo (e ele pode ter uma API diferente de
malloc
&free
....).fonte
gcc -flto -Os
? ) e você pode ganhar um pouco de memória ....As pessoas tendem a ser cautelosas com uma pilha grande, porque ela cresce na RAM e sobrescreve os valores das variáveis, levando a um comportamento inexplicável. Isso fica ainda pior, porque você precisa conhecer o menor endereço possível do ponteiro da pilha e subtrair o tamanho a ser alocado ao entrar na rotina.
Tudo isso é um trabalho para o gerenciamento de memória de hardware (deve gerar traps ou falhas quando ocorre um estouro de pilha) ou para o compilador, pois possui recursos para esse tipo de análise.
Caso contrário, você pode fazer o que quiser com sua RAM.
fonte
Como as respostas anteriores apontaram, também recomendo deixar a matriz estática se ela se encaixar na memória. Na maioria dos casos, é muito mais importante ter uma pegada determinística na memória, mesmo que isso signifique que você "desperdiça" memória para variáveis que não são usadas o tempo todo. Colocar matrizes grandes em sua pilha é muito fácil de explodir, e os estouros de pilha tendem a causar problemas difíceis de encontrar e de reproduzir (se você não pode usar o MMU para proteger a pilha).
A sugestão de compartilhar o bloco com outros dados com a união é válida para IMO, embora também possa ser fonte de problemas difíceis de encontrar, se você localizar variáveis erradas nele.
Se você estiver ficando sem memória e precisar desesperadamente criar variáveis de vida mais curta para compartilhá-la, antes de mover a matriz para a pilha, consideraria adicionar alocação dinâmica de memória, mesmo que ela tenha suas próprias desvantagens. Nesse caso, pode não ser uma resposta, pois o array parece bastante grande comparado à memória disponível.
fonte
Você tem outra opção se tiver algum tipo de armazenamento flash. Você pode trocar a velocidade de acesso por memória RAM armazenando seus dados em flash e lendo e pesquisando lá. Você só precisaria carregar um registro de cada vez no ram. Será um pouco mais complicado se você precisar atualizar os registros. Você precisará de um mecanismo segmentado e nivelado ao desgaste. Eu fiz isso no passado e incluí um índice para acelerar o acesso.
fonte
Especialmente ao trabalhar com sistemas embarcados, você deseja que o máximo possível de falhas ocorra no momento da compilação e nada falhe em tempo de execução (bom, se conseguirmos isso, no entanto ...).
A criação de grandes matrizes que você pode precisar em estados arbitrários do seu programa alocado estaticamente faz exatamente isso - o vinculador acabará alertando você "isso não se encaixa na RAM", enquanto a alocação da pilha simplesmente faria o seu programa travar com uma pilha difícil de depurar transborda.
fonte