Eu gostaria de saber quanta RAM eu estou usando no meu projeto, tanto quanto posso dizer, não há como realmente resolver isso (além de passar por mim e calculá-la). Eu cheguei a um estágio em um projeto bastante grande, onde determinei que estou ficando sem memória RAM.
Eu determinei isso porque posso adicionar uma seção e, em seguida, todo o inferno se solta em outro lugar do meu código sem motivo aparente. Se eu #ifndef
algo mais, funciona novamente. Não há nada programaticamente errado com o novo código.
Suspeitei por um tempo que estava chegando ao fim da RAM disponível. Eu não acho que estou usando muita pilha (embora seja possível), qual é a melhor maneira de determinar quanta RAM eu estou realmente usando?
Passando e tentando resolver isso, tenho problemas quando chego a enumerações e estruturas; quanta memória eles custam?
primeira edição: TAMBÉM editei muito meu esboço desde o início, esses não são os resultados reais que obtive inicialmente, mas são o que estou obtendo agora.
text data bss dec hex filename
17554 844 449 18847 499f HA15_20140317w.cpp.elf
16316 694 409 17419 440b HA15_20140317w.cpp.elf
17346 790 426 18562 4882 HA15_20140317w.cpp.elf
A primeira linha (com texto 17554) não estava funcionando. Após muita edição, a segunda linha (com texto 16316) está funcionando como deveria.
edit: a terceira linha tem tudo funcionando, leitura serial, minhas novas funções, etc. Eu essencialmente removi algumas variáveis globais e código duplicado. Menciono isso porque (como se suspeita) não se trata desse código em si, mas do uso da RAM. O que me leva de volta à pergunta original: "como melhor mensurá-la"? Ainda estou consultando algumas respostas, obrigado.
Como realmente interpreto as informações acima?
Até agora, meu entendimento é:
`TEXT` is program instruction memory
`DATA` is variables (unitialised?) in program memory
`BSS` is variables occupying RAM
como o BSS é consideravelmente menor que 1024 bytes, por que o segundo funciona, mas o primeiro não? Se for, DATA+BSS
ambos ocupam mais de 1024.
reedição: editei a pergunta para incluir o código, mas agora a removi porque realmente não tinha nada a ver com o problema (exceto talvez práticas ruins de codificação, declarações de variáveis e similares). Você pode revisar o código revisando as edições, se realmente quiser vê-lo. Eu queria voltar à pergunta em questão, que era mais baseada em: Como medir o uso da RAM.
String
tipo em seus programas? Sabe-se que ele executa alocações e liberações de memória dinâmica freqüentes, o que pode fragmentar o heap até o ponto em que você não pode ficar com mais memória.String
s por causa da sobrecarga. Estou feliz por trabalhar com matrizes de caracteres, isto é, quase sempre defino todas as minhas matrizes de caracteres com um tamanho fixo (no momento, tenho uma matriz de UM byte que não é apenas porque altero o comprimento do conteúdo para diferentes recompilações.Respostas:
Você pode usar as funções fornecidas AVRGCC: Monitorando o uso da pilha
A função foi planejada para verificar o uso da pilha, mas o que é relatado é a RAM real que nunca foi usada (durante a execução). Isso é feito "pintando" (preenchendo) a RAM com um valor conhecido (0xC5) e, em seguida, verificando a área da RAM contando quantos bytes ainda têm o mesmo valor inicial.
O relatório mostrará a RAM que não foi usada (mínimo de RAM livre) e, portanto, você poderá calcular o máximo de RAM que foi usado (RAM total - RAM relatada).
Existem duas funções:
StackPaint é executado automaticamente durante a inicialização e "pinta" a RAM com o valor 0xC5 (pode ser alterado, se necessário).
StackCount pode ser chamado a qualquer momento para contar a RAM que não foi usada.
Aqui está um exemplo de uso. Não faz muito, mas pretende mostrar como usar as funções.
fonte
Os principais problemas que você pode ter com o uso de memória em tempo de execução são:
malloc
ounew
)Ambos são realmente os mesmos que o AVR SRAM (2K no Arduino) é usado para ambos (além de dados estáticos cujo tamanho nunca muda durante a execução do programa).
Geralmente, a alocação dinâmica de memória raramente é usada em MCUs, apenas algumas bibliotecas normalmente a usam (uma delas é a
String
classe, que você mencionou que não usa, e esse é um bom ponto).A pilha e a pilha podem ser vistas na figura abaixo (cortesia de Adafruit ):
Portanto, o problema mais esperado vem do estouro de pilha (ou seja, quando a pilha cresce em direção ao heap e transborda sobre ele e, se o heap não foi usado, transborda na zona de dados estáticos da SRAM. você tem um alto risco de:
Para saber a quantidade de memória que resta entre a parte superior da pilha e a parte superior da pilha (na verdade, podemos chamá-la de parte inferior se representarmos a pilha e a pilha na mesma imagem mostrada abaixo), você pode usar a seguinte função:
No código acima,
__brkval
aponta para o topo do heap, mas é0
quando o heap não foi usado; nesse caso, usamos para&__heap_start
onde aponta__heap_start
, a primeira variável que marca a parte inferior do heap;&v
é claro que está no topo da pilha (esta é a última variável pressionada na pilha); portanto, a fórmula acima retorna a quantidade de memória disponível para a pilha (ou a pilha se você a usar) crescer.Você pode usar esta função em vários locais do seu código para tentar descobrir onde esse tamanho está ficando drasticamente reduzido.
Obviamente, se você vir esta função retornar um número negativo, será tarde demais: você já ultrapassou a pilha!
fonte
malloc
enew
a única maneira de fazer isso? Nesse caso, não tenho nada dinâmico. Além disso, eu acabei de aprender que a ONU tem 2K de SRAM. Eu pensei que era 1K. Levando isso em consideração, não estou ficando sem memória RAM! Eu preciso procurar em outro lugar.calloc
. Mas você pode estar usando 3º libs partido que usam alocação dinâmica sem você saber (você teria que verificar o código-fonte de todas as suas dependências para ter certeza sobre isso)Ao descobrir como localizar o arquivo .elf gerado em seu diretório temporário, você pode executar o comando abaixo para despejar um uso da SRAM, onde
project.elf
deve ser substituído pelo.elf
arquivo gerado . A vantagem dessa saída é a capacidade de inspecionar como sua SRAM é usada. Todas as variáveis precisam ser globais, são realmente todas necessárias?Observe que isso não mostra o uso da pilha ou da memória dinâmica, como Ignacio Vazquez-Abrams observou nos comentários abaixo.
Além disso, um
avr-objdump -S -j .data project.elf
pode ser verificado, mas nenhum dos meus programas produz nada com isso, então não sei ao certo se é útil. Ele deveria listar 'dados inicializados (diferentes de zero)'.fonte
avr-size
. Mas isso não mostra alocações dinâmicas ou uso de pilha.avr-objdump
eavr-size
editarei minha postagem acima em breve. Obrigado por isso.Seria melhor usar uma combinação de estimativa manual e usando o
sizeof
operador. Se todas as suas declarações forem estáticas, isso deve fornecer uma imagem precisa.Se você estiver usando alocações dinâmicas, poderá encontrar um problema assim que começar a desalocar a memória. Isso ocorre devido à fragmentação da memória no heap.
Um enum ocupa tanto espaço quanto um
int
. Portanto, se você tiver um conjunto de 10 elementos em umaenum
declaração, seria isso10*sizeof(int)
. Além disso, toda variável que usa uma enumeração é simplesmente umint
.Para estruturas, seria mais fácil usar
sizeof
para descobrir. As estruturas ocupam um espaço (mínimo) igual à soma de seus membros. Se o compilador estruturar o alinhamento, pode ser mais, porém isso é improvável no caso deavr-gcc
.fonte
sizeof
para esse fim. No momento, já tenho quase 400 bytes (globalmente). Agora vou analisar e calcular as enumerações (manualmente) e as estruturas (das quais tenho algumas - e vou usarsizeof
), e relatar.sizeof
saber o tamanho dos seus dados estáticos, pois isso é impresso pelo avrdude IIRC.Existe um programa chamado Arduino Builder que fornece uma visualização clara da quantidade de flash, SRAM e EEPROM que seu programa está usando.
O construtor Arduino faz parte do solução IDE CodeBlocks Arduino . Ele pode ser usado como um programa independente ou através do IDE CodeBlocks Arduino.
Infelizmente, o Arduino Builder é um pouco antigo, mas deve funcionar na maioria dos programas e na maioria dos Arduinos, como o Uno.
fonte