Me gustaría saber cuánta RAM estoy usando en mi proyecto, por lo que puedo decir, no hay forma de resolverlo (aparte de calcularlo yo mismo). Llegué a una etapa en un proyecto bastante grande donde he determinado que me estoy quedando sin RAM.
He determinado esto porque puedo agregar una sección y luego todo el infierno se desata en otro lugar en mi código sin razón aparente. Si salgo #ifndef
algo más, funciona de nuevo. No hay nada programáticamente incorrecto con el nuevo código.
Sospeché por un tiempo que estaba llegando al final de la RAM disponible. No creo que esté usando demasiada pila (aunque es posible), ¿cuál es la mejor manera de determinar cuánta RAM estoy usando realmente?
Revisando e intentando resolverlo, tengo problemas cuando llego a enumeraciones y estructuras; ¿Cuánta memoria cuestan?
primera edición: TAMBIÉN, he editado mucho mi boceto desde que comencé, estos no son los resultados reales que obtuve inicialmente, pero son lo que estoy obteniendo ahora.
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
La primera línea (con el texto 17554) no funcionaba, después de mucha edición, la segunda línea (con el texto 16316) funciona como debería.
editar: la tercera línea tiene todo funcionando, lectura en serie, mis nuevas funciones, etc. Básicamente eliminé algunas variables globales y código duplicado. Menciono esto porque (como se sospecha) no se trata de este código en sí, sino del uso de RAM. Lo que me lleva de vuelta a la pregunta original, "cómo medirla mejor" Todavía estoy revisando algunas respuestas, gracias.
¿Cómo interpreto realmente la información anterior?
Hasta ahora mi entendimiento es:
`TEXT` is program instruction memory
`DATA` is variables (unitialised?) in program memory
`BSS` is variables occupying RAM
dado que BSS es considerablemente menor que 1024 bytes, ¿por qué funciona el segundo, pero el primero no? Si es así, DATA+BSS
ambos ocupan más de 1024.
reeditar: edité la pregunta para incluir el código, pero ahora la eliminé porque realmente no tenía nada que ver con el problema (aparte de quizás prácticas de codificación deficientes, declaraciones de variables y similares). Puede revisar el código al revisar las ediciones si realmente desea verlo. Quería volver a la pregunta en cuestión, que estaba más basada en: Cómo medir el uso de RAM.
String
escribir en tus programas? Se sabe que esto realiza asignaciones y lanzamientos frecuentes de memoria dinámica, que pueden fragmentar el montón hasta el punto en que no queda memoria.String
s debido a la sobrecarga. Estoy trabajando feliz con arrays de char, dicho esto, casi siempre me defino todos mis arreglos de char con un tamaño fijo (por el momento, tengo una matriz de bytes que no es puramente debido a que cambie la longitud del contenido para diferentes recompilaciones.Respuestas:
Puede usar las funciones proporcionadas AVRGCC: Monitoreo del uso de la pila
La función estaba destinada a verificar el uso de la pila, pero lo que informa es la RAM real que nunca se ha utilizado (durante la ejecución). Lo hace "pintando" (llenando) la RAM con un valor conocido (0xC5), y luego verificando el área de RAM contando cuántos bytes todavía tienen el mismo valor inicial.
El informe mostrará la RAM que no se ha utilizado (RAM libre mínima) y, por lo tanto, puede calcular la RAM máxima que se ha utilizado (RAM total - RAM informada).
Hay dos funciones:
StackPaint se ejecuta automáticamente durante la inicialización y "pinta" la RAM con el valor 0xC5 (se puede cambiar si es necesario).
Se puede llamar a StackCount en cualquier momento para contar la RAM que no se ha utilizado.
Aquí hay un ejemplo de uso. No hace mucho, pero tiene la intención de mostrar cómo usar las funciones.
fuente
Los principales problemas que puede tener con el uso de memoria en tiempo de ejecución son:
malloc
onew
)Ambos son en realidad lo mismo que el AVR SRAM (2K en Arduino) se usa para ambos (además de los datos estáticos cuyo tamaño nunca cambia durante la ejecución del programa).
En general, la asignación de memoria dinámica rara vez se usa en MCU, solo unas pocas bibliotecas suelen usarla (una de ellas es la
String
clase, que mencionó que no usa, y ese es un buen punto).La pila y el montón se pueden ver en la imagen a continuación (cortesía de Adafruit ):
Por lo tanto, el problema más esperado proviene del desbordamiento de la pila (es decir, cuando la pila crece hacia el montón y se desborda en él, y luego, si el montón no se usó en absoluto, se desborda en la zona de datos estáticos de la SRAM. En ese momento, tiene un alto riesgo de:
Para saber la cantidad de memoria que queda entre la parte superior del montón y la parte superior de la pila (en realidad, podríamos llamarla la parte inferior si representamos tanto el montón como la pila en la misma imagen que se muestra a continuación), puede usar la siguiente función:
En el código anterior,
__brkval
apunta a la parte superior del montón pero es0
cuando el montón no se ha utilizado, en cuyo caso usamos&__heap_start
qué puntos__heap_start
, la primera variable que marca la parte inferior del montón;&v
puntos, por supuesto, a la parte superior de la pila (esta es la última variable introducida en la pila), por lo tanto, la fórmula anterior devuelve la cantidad de memoria disponible para la pila (o el montón si la usa) para crecer.Puede usar esta función en varias ubicaciones de su código para intentar descubrir dónde se reduce drásticamente este tamaño.
Por supuesto, si alguna vez ve que esta función devuelve un número negativo, es demasiado tarde: ¡ya ha desbordado la pila!
fuente
malloc
ynew
la única forma en que puedo hacer eso? Si es así, entonces no tengo nada dinámico. Además, acabo de enterarme de que UNO tiene 2K de SRAM. Pensé que era 1K. Teniendo esto en cuenta, ¡no me estoy quedando sin RAM! Necesito buscar en otro lado.calloc
. Pero puede estar usando librerías de terceros que usan la asignación dinámica sin que usted lo sepa (tendría que verificar el código fuente de todas sus dependencias para estar seguro)Cuando descubra cómo ubicar el archivo .elf generado en su directorio temporal, puede ejecutar el siguiente comando para volcar un uso de SRAM, donde
project.elf
se reemplazará con el.elf
archivo generado . La ventaja de esta salida es la capacidad de inspeccionar cómo se usa su SRAM. ¿Todas las variables deben ser globales, son realmente necesarias?Tenga en cuenta que esto no muestra el uso de la pila o la memoria dinámica como lo señaló Ignacio Vázquez-Abrams en los comentarios a continuación.
Además,
avr-objdump -S -j .data project.elf
se puede verificar a, pero ninguno de mis programas genera nada con eso, así que no puedo decir con certeza si es útil. Se supone que enumera 'datos inicializados (no cero)'.fuente
avr-size
. Pero eso no le mostrará las asignaciones dinámicas o el uso de la pila.avr-objdump
yavr-size
editaré mi publicación anterior en breve. Gracias por esto.Sería mejor utilizar una combinación de estimación manual y utilizando el
sizeof
operador. Si todas sus declaraciones son estáticas, esto debería proporcionarle una imagen precisa.Si está utilizando asignaciones dinámicas, puede encontrarse con un problema una vez que comience a desasignar la memoria. Esto se debe a la fragmentación de la memoria en el montón.
Una enumeración ocupa tanto espacio como una
int
. Entonces, si tiene un conjunto de 10 elementos en unaenum
declaración, sería10*sizeof(int)
. Además, cada variable que usa una enumeración es simplemente unint
.Para las estructuras, sería más fácil de usar
sizeof
para averiguarlo. Las estructuras ocupan un espacio (mínimo) igual a la suma de sus miembros. Si el compilador estructura la alineación, entonces puede ser más, sin embargo, esto es poco probable en el caso deavr-gcc
.fuente
sizeof
para este propósito. Por el momento, tengo casi 400 bytes ya representados (globalmente). Ahora revisaré y calcularé las enumeraciones (manualmente) y las estructuras (de las cuales tengo algunas, y las usarésizeof
), e informaré de nuevo.sizeof
saber el tamaño de sus datos estáticos, ya que esto es impreso por avrdude IIRC.Hay un programa llamado Arduino Builder que proporciona una visualización clara de la cantidad de flash, SRAM y EEPROM que está utilizando su programa.
El constructor Arduino forma parte de la solución CodeBlocks Arduino IDE . Se puede utilizar como un programa independiente o a través del IDE de CodeBlocks Arduino.
Desafortunadamente, Arduino Builder es un poco viejo, pero debería funcionar para la mayoría de los programas y la mayoría de los Arduinos, como el Uno.
fuente