¿Cómo comprimir el código para obtener más Flash y RAM? [cerrado]

14

He trabajado en el desarrollo de una función en un producto en particular nuestro. Ha habido una solicitud para portar la misma función a otro producto. Este producto se basa en un microcontrolador M16C, que tradicionalmente tiene 64K Flash y 2k de RAM.

Es un producto maduro, y por lo tanto, solo le quedan 132 bytes de Flash y 2 bytes de RAM.

Para portar la característica solicitada (la característica en sí ha sido optimizada), necesito 1400 bytes de Flash y ~ 200 Bytes de RAM.

¿Alguien tiene alguna sugerencia sobre cómo recuperar estos Bytes por compactación de código? ¿Qué cosas específicas busco cuando intento compactar un código de trabajo ya existente?

Cualquier idea será realmente apreciada.

Gracias.

    
pregunta IntelliChick

17 respuestas

18

Tiene un par de opciones: primero es buscar código redundante y moverlo a una sola llamada para deshacerse de la duplicación; El segundo es eliminar la funcionalidad.

Observe detenidamente su archivo .map y vea si hay funciones que pueda eliminar o reescribir. Asegúrese también de que las llamadas de biblioteca que se están utilizando son realmente necesarias.

Ciertas cosas como la división y las multiplicaciones pueden traer una gran cantidad de código, pero el uso de turnos y un mejor uso de las constantes pueden hacer que el código sea más pequeño. También eche un vistazo a cosas como constantes de cadena y printf s. Por ejemplo, cada printf consumirá tu ROM, pero es posible que puedas tener un par de cadenas de formato compartido en lugar de repetir esa constante de cadena una y otra vez.

Para la memoria, vea si puede deshacerse de los globales y, en su lugar, utilice autos en una función. También evite tantas variables en la función principal como sea posible, ya que consumen memoria al igual que lo hacen los globals.

    
respondido por el Rex Logan
8

Siempre vale la pena mirar la salida del archivo de lista (ensamblador) para buscar cosas en las que tu compilador en particular es particularmente malo.

Por ejemplo, es posible que las variables locales sean muy caras y si la aplicación es lo suficientemente simple como para que valga la pena arriesgarse, mover algunos contadores de bucle a variables estáticas puede ahorrar mucho código.

O la indexación de matrices puede ser muy costosa pero las operaciones de puntero son mucho más baratas. O viceversa.

Pero mirar el lenguaje ensamblador es el primer paso.

    
respondido por el user1844
8

Las optimizaciones del compilador, por ejemplo, -Os en GCC dan el mejor equilibrio entre la velocidad y el tamaño del código. Evite -O3 , ya que puede aumentar el tamaño del código.

    
respondido por el Thomas O
8

Para RAM, verifique el rango de todas sus variables. ¿Está usando ints donde podría usar un char? ¿Los búferes son más grandes de lo que deberían ser?

La compresión de código es muy dependiente de la aplicación y del estilo de codificación. Sus cantidades dejadas sugieren que tal vez el código ya se ha ido a través de algunas restricciones, lo que puede significar que no queda mucho más.

También analice detenidamente la funcionalidad general: ¿hay algo que no se use realmente y se pueda desechar?

    
respondido por el mikeselectricstuff
8

Si es un proyecto antiguo pero el compilador se ha desarrollado desde entonces, podría ser que un compilador más reciente produzca un código más pequeño

    
respondido por el mikeselectricstuff
7

Siempre vale la pena consultar el manual del compilador para ver las opciones para optimizar el espacio.

Para gcc -ffunction-sections y -fdata-sections con el indicador del enlazador --gc-sections son buenos para eliminar el código muerto.

Aquí hay otros excelentes consejos (orientados hacia el AVR)

    
respondido por el Toby Jaffey
6

Puede examinar la cantidad de espacio de pila y espacio de pila que se asignan. Es posible que pueda recuperar una cantidad sustancial de RAM si uno de estos o ambos están sobre asignados.

Mi conjetura es que para un proyecto que se ajuste a 2k de RAM para comenzar, no hay una asignación de memoria dinámica (uso de malloc , calloc , etc.). Si este es el caso, puede deshacerse de su montón asumiendo que el autor original dejó un poco de RAM asignada para el montón.

Tienes que tener mucho cuidado al reducir el tamaño de la pila, ya que esto puede causar errores que son muy difíciles de encontrar. Puede ser útil comenzar inicializando todo el espacio de la pila a un valor conocido (algo distinto a 0x00 o 0xff, ya que estos valores comúnmente ya existen) y luego ejecutar el sistema por un tiempo para ver cuánto espacio de pila no se usa.

    
respondido por el semaj
5

¿Su código usa matemática de punto flotante? Es posible que pueda volver a implementar sus algoritmos utilizando solo números enteros y eliminar los gastos generales de usar la biblioteca de punto flotante de C. P.ej. en algunas aplicaciones, funciones como sine, log, exp pueden reemplazarse por aproximaciones polinomiales enteras.

¿Su código utiliza tablas de consulta grandes para algún algoritmo, como los cálculos de CRC? Puede intentar sustituir una versión diferente del algoritmo que calcula los valores sobre la marcha, en lugar de usar las tablas de consulta. La advertencia es que el algoritmo más pequeño es probablemente más lento, así que asegúrate de tener suficientes ciclos de CPU.

¿Tiene su código grandes cantidades de datos constantes, como tablas de cadenas, páginas HTML o gráficos de píxeles (iconos)? Si es lo suficientemente grande (digamos 10 kB), podría valer la pena implementar un esquema de compresión muy simple para reducir los datos y descomprimirlos sobre la marcha cuando sea necesario.

    
respondido por el Craig McQueen
2

Puedes intentar reorganizarlo para codificar mucho, a un estilo más compacto. Depende mucho de lo que esté haciendo el código. La clave es encontrar cosas similares y volverlas a implementar en términos de cada uno. Un extremo sería utilizar un lenguaje de nivel superior, como Forth, con el cual puede ser más fácil lograr una mayor densidad de código que en C o ensamblador.

Aquí está Forth para M16C .

    
respondido por el Prof. Falken
2

Establece el nivel de optimización del compilador. Muchos IDE tienen configuraciones que permiten optimizaciones de tamaño de código a expensas del tiempo de compilación (o incluso tiempo de procesamiento en algunos casos). Pueden lograr la compactación de código volviendo a ejecutar su optimizador un par de veces, buscando patrones optimizables menos comunes y una gran cantidad de trucos que pueden no ser necesarios para la compilación casual / debug. Por lo general, de forma predeterminada, los compiladores se configuran en un nivel medio de optimización. Busque en la configuración y debería poder encontrar alguna escala de optimización basada en enteros.

    
respondido por el Joel B
2

Si ya está utilizando un compilador de nivel profesional como IAR, creo que tendrá dificultades para obtener ahorros importantes con pequeños ajustes de código de bajo nivel; tendrá que buscar más para eliminar la funcionalidad. o haciendo reescrituras importantes de partes de una manera más eficiente. Tendrá que ser un programador más inteligente que el que escribió la versión original ... En cuanto a la RAM, debe analizar detenidamente cómo se usa actualmente y ver si hay posibilidades de superponer el uso de la misma RAM para diferentes cosas en diferentes momentos (las uniones son útiles para esto). Los tamaños de pila y pila predeterminados de IAR en los ARM / AVR que he tendido a ser demasiado generosos, por lo que sería lo primero que se debe tener en cuenta.

    
respondido por el mikeselectricstuff
2

Algo más que se debe comprobar: algunos compiladores en algunas arquitecturas copian las constantes en la RAM: generalmente se usan cuando el acceso a las constantes de flash es lento / difícil (por ejemplo, AVR) p.ej. El compilador AVR de IAR requiere un calificador _flash para no copiar una constante en la RAM)

    
respondido por el mikeselectricstuff
1

Si su procesador no tiene soporte de hardware para un parámetro / pila local pero el compilador intenta implementar una pila de parámetros en tiempo de ejecución de todos modos, y si su código no necesita ser reingresado, es posible que pueda para ahorrar espacio de código mediante la asignación estática de variables automáticas. En algunos casos, esto debe hacerse manualmente; En otros casos, las directivas del compilador pueden hacerlo. Una asignación manual eficiente requerirá compartir las variables entre las rutinas. Dicho intercambio debe hacerse con cuidado, para asegurar que ninguna rutina use una variable que otra rutina considere "dentro del alcance", pero en algunos casos los beneficios de tamaño de código pueden ser significativos.

Algunos procesadores tienen convenciones de llamada que pueden hacer que algunos estilos de paso de parámetros sean más eficientes que otros. Por ejemplo, en los controladores PIC18, si una rutina toma un solo parámetro de un byte, se puede pasar en un registro; si se necesita más que eso, todos los parámetros deben pasarse en la RAM. Si una rutina toma dos parámetros de un byte, puede ser más eficiente "pasar" uno en una variable global, y luego pasar el otro como parámetro. Con las rutinas ampliamente utilizadas, los ahorros pueden sumarse. Pueden ser especialmente significativos si el parámetro pasado a través de global es un indicador de un solo bit, o si normalmente tendrá un valor de 0 o 255 (ya que existen instrucciones especiales para almacenar un 0 o 255 en la RAM).

En el ARM, colocar variables globales que se usan con frecuencia en una estructura puede reducir significativamente el tamaño del código y mejorar el rendimiento. Si A, B, C, D y E son variables globales separadas, entonces el código que los usa a todos debe cargar la dirección de cada uno en un registro; Si no hay suficientes registros, puede ser necesario volver a cargar esas direcciones varias veces. Por el contrario, si forman parte de la misma estructura global MyStuff, entonces el código que utiliza MyStuff.A, MyStuff.B, etc. puede simplemente cargar la dirección de MyStuff una vez. Gran victoria.

    
respondido por el supercat
1

1.Si tu código se basa en muchas estructuras, asegúrate de que los miembros de la estructura estén ordenados de los que ocupan la mayor parte de la memoria.

Ej: "uint32_t uint16_t uint8_t" en lugar de "uint16_t uint8_t uint32_t"

Esto asegurará el relleno mínimo de la estructura.

2.Use const para las variables cuando corresponda. Esto asegurará que esas variables estén en la ROM y no consuman RAM

    
respondido por el Akshay Immanuel D
1

Algunos trucos (quizás obvios) que he usado con éxito para comprimir el código de algunos clientes:

  1. Compacta banderas en campos de bits o máscaras de bits. Esto puede ser beneficioso, ya que normalmente los booleanos se almacenan como enteros, desperdiciando así la memoria. Esto ahorrará tanto RAM como ROM y normalmente no lo hace el compilador.

  2. Busque redundancia en el código y use bucles o funciones para ejecutar declaraciones repetidas.

  3. También he guardado algo de ROM al reemplazar muchas declaraciones if(x==enum_entry) <assignment> de constantes con una matriz indexada, teniendo cuidado de que las entradas de enumeración se puedan usar como índice de matriz

respondido por el clabacchio
0

Si puede, use funciones en línea o macros del compilador en lugar de funciones pequeñas. Hay una sobrecarga de tamaño y velocidad con argumentos que pasan y tal que puede remediarse haciendo que la función esté en línea.

    
respondido por el AngryEE
0

Cambie las variables locales para que tengan el mismo tamaño de sus registros de CPU.

Si la CPU es de 32 bits, use variables de 32 bits incluso si el valor máximo nunca superará los 255. Si usó una variable de 8 bits, el compilador agregará código para enmascarar los 24 bits superiores.

El primer lugar que buscaría es en las variables de bucle for.

for( i = 0; i < 100; i++ )

Esto puede parecer un buen lugar para una variable de 8 bits, pero una variable de 32 bits puede producir menos código.

    
respondido por el Robert

Lea otras preguntas en las etiquetas