2009-03-19 10 views
11

Tengo una aplicación de firmware heredada que requiere una nueva funcionalidad. El tamaño de la aplicación ya se acercaba a la capacidad de flash limitada del dispositivo y las pocas funciones y variables nuevas lo empujaron por el borde. Activar la optimización del compilador es el truco, pero el cliente es cauteloso de hacerlo porque han causado fallas en el pasado. Entonces, ¿cuáles son algunas cosas comunes a tener en cuenta cuando se refactoriza el código C para producir una salida más pequeña?¿Cuáles son algunos métodos de refactorización para reducir el tamaño del código compilado?

+0

Creo que la pregunta debería decir "uso de memoria" o "tamaño de salida" en lugar de "tamaño de código". – Angel

+0

El uso de memoria no es el problema. Es el tamaño de la imagen que se escribe en la memoria flash interna de la MCU. –

+0

Creo que el "tamaño del código compilado" es menos ambiguo. –

Respuesta

17
  • funciones Uso de generación en lugar de tablas de datos cuando sea posible
  • funciones en línea Desactivar
  • vez se utilizan con frecuencia macros en funciones
  • Reducir la resolución de las variables más grandes que el tamaño de la máquina nativo (es decir, 8 bits de micro, tratar de deshacerse de las variables de 16 y 32 bits - duplica y cuadruplica algunas secuencias de código)
  • Si el micro tiene un conjunto de instrucciones más pequeño (pulgar de brazo) habilítelo en el compilador
  • Si la memoria ORY está segmentado (es decir, paginados o no lineal) a continuación
    • código Reorganizar para que un menor número de llamadas globales (instrucciones de llamada más grandes) necesitan ser utilizados
    • código de reorganizar y uso de variables para eliminar la memoria global exige
    • Re- evaluar el uso de memoria mundial - si se puede colocar en la pila a continuación, tanto mejor
  • Asegúrese de que está compilando con depuración desactivado - en algunos procesadores que hace una gran diferencia
  • datos
  • Comprimir que puede no se generará n the fly - luego descomprimir en ram al inicio para un acceso rápido
  • Profundizar en las opciones del compilador - puede ser que cada llamada sea automáticamente global, pero es posible que pueda deshabilitarla de manera segura archivo por archivo para reducir el tamaño (a veces significativamente)

Si aún necesita más espacio que con compile with optimizations encendido, mire el ensamblaje generado en comparación con el código no optimizado. A continuación, vuelva a escribir el código donde tuvieron lugar los cambios más importantes para que el compilador genere las mismas optimizaciones basadas en las reescrituras difíciles de C con la optimización desactivada.

Por ejemplo, es posible tener varios 'si' las declaraciones que hacen comparaciones similares:

if(A && B && (C || D)){} 
if(A && !B && (C || D)){} 
if(!A && B && (C || D)){} 

luego la creación de nuevo variable y hacen algunas comparaciones con antelación salvará el compilador de código de duplicación:

E = (C || D); 

if(A && B && E){} 
if(A && !B && E){} 
if(!A && B && E){} 

Esta es una de las optimizaciones que el compilador hace por usted automáticamente si la enciende. Hay muchos, muchos otros, y puede considerar leer un poco de teoría del compilador si quiere aprender a hacerlo a mano en el código C.

+1

La única advertencia es que cualquiera de estos procesos en secciones de tiempo crítico requerirá pruebas adicionales. –

+0

Supongo que debería agregar un descargo de responsabilidad, pero la realidad es que cualquier cambio en estas líneas es el tamaño de negociación para el rendimiento. –

+0

Si deshabilita las funciones en línea y convierte las macros en funciones, ¿no está aumentando el uso de la memoria de tiempo de ejecución (más llamadas a funciones = nuevas estructuras)? Aunque no estoy seguro de esto. – DevinB

2

Refactorear duplicate code debería tener el mayor impacto en la memoria de su programa.

0

Preste atención a las macros. Pueden producir una gran cantidad de código a partir de una sola macro expansión. Si encuentra tales macros, intente reescribirlas para que su tamaño se minimice y la funcionalidad se mueva a funciones.

Preste atención al código duplicado, tanto copiado como duplicado lógicamente. Intenta separar el código duplicado en funciones.

Comprueba si el compilador admite la alineación y se puede desactivar.

0

Optimización del compilador que provoca errores? Eso es extraño. Obtenga un mapa de su programa y vea si debe orientar los datos o el código. Busque el código duplicado. Busque el código con un objetivo similar. Un ejemplo de esto es el código busybox, que apunta a una pequeña huella de memoria.

Está favoreciendo el tamaño sobre la legibilidad, por lo que a veces se pone bastante feo, con gotos y demás.

+1

No es nada raro que los compiladores integrados tengan errores. Cuanto menos utilizado sea el chip, más probable es que el compilador tenga problemas. Esto es menos común hoy en día que en el pasado (especialmente si gcc es su compilador), pero todavía es una preocupación para las plataformas poco comunes (menos probadas). –

7

En general, haga uso de su mapa o herramientas de engarce para descubrir cuáles son sus símbolos más grandes/más numerosos, y luego, posiblemente, écheles un vistazo utilizando un desensamblador. Te sorprendería lo que encuentres de esta manera.

Con un poco de perl o similar, puede hacer un trabajo corto de un archivo .xMAP o los resultados de "objdump" o "nm", y volver a ordenarlo de varias formas para obtener información pertinente.


Específico para pequeños conjuntos de instrucciones: reloj para literal pool uso. Al cambiar de, por ejemplo, la instrucción ARM (32 bits por instrucción) establecida en el conjunto de instrucciones THUMB (16 bits por instrucción) puede ser útil en algunos procesadores ARM, reduce el tamaño del campo "inmediato".

De repente, algo que sería una carga directa desde un nivel global o estático se vuelve muy indirecto; primero debe cargar la dirección del global/estático en un registro, luego cargar desde allí, en lugar de solo codificar la dirección directamente en la instrucción. Así que obtienes unas instrucciones adicionales y una entrada adicional en el grupo literal para algo que normalmente hubiera sido una instrucción.

Una estrategia para combatir esto es agrupar los elementos globales y la estática en estructuras; de esta forma, solo almacena un literal (la dirección de su estructura global) y calcula los desplazamientos a partir de eso, en lugar de almacenar muchos literales diferentes cuando accede a múltiples estáticas/globales.

Convertimos nuestras clases de "singleton" de administrar sus propios punteros de instancia a solo miembros en una gran "struct GlobalTable", y hace una diferencia notable en el tamaño del código (un pequeño porcentaje) así como el rendimiento en algunos casos .


De lo contrario: mantener un ojo para estructuras estáticas y matrices de datos construidos no trivial. Cada uno de estos típicamente genera grandes cantidades de código .sinit ("funciones invisibles", si se quiere) que se ejecutan antes de main() para poblar estas matrices correctamente. Si puedes usar solo tipos de datos triviales en tu estática, estarás mucho mejor.

Esto es otra vez algo que se puede identificar fácilmente mediante el uso de una herramienta sobre los resultados de "nm" u "objdump" o similar. Si tienes un montón de cosas .sinit, ¡querrás investigar!


Ah, y - si su compilador/enlazador lo soporta, no tenga miedo a habilitar selectivamente la optimización o conjuntos de instrucciones más pequeños por sólo ciertos archivos o funciones!

+0

+1 Un buen acercamiento al caucho. –

+0

+1 El archivo de mapa del enlazador es el lugar para comenzar. Le mostrará dónde se está utilizando el espacio. –

0

Las respuestas anteriores afirman "Activar la optimización del compilador [redujo el tamaño del código]".Dada toda la documentación y la experiencia que he tenido en los sistemas integrados programación de TI DSP, sé con certeza que al activar la optimización aumentará el tamaño del código (para el chip DSP de TI).


Me explico:

La TI TMSCx6416 DSP tiene 9 opciones del compilador que afectarán a su tamaño de código.

  1. 3 banderas diferentes para la optimización
  2. 3 banderas diferentes para la depuración
  3. 3 banderas diferentes para el tamaño del Código

Por mi compilador, cuando se enciende el nivel de optimización tres los estados de documentación:

  1. Se producirá la alineación automática para ciertas funciones -> aumentará el tamaño del código
  2. 012 canalización
  3. software está activada -> aumentará el tamaño del código

¿Qué es la segmentación software?

Ahí es donde el compilador hará cosas en el ensamblaje que hacen que los bucles for se ejecuten significativamente más rápido (hasta un par de veces más rápido) pero a costa de un mayor tamaño de código. Sugiero leer sobre software pipelining at wikipedia (busque desenrollar, prólogo y epilog).

Así que revise su documentación para asegurarse de que la optimización no hace que su código sea más grande.


Otra sugerencia es buscar opciones del compilador que se relacionan con el tamaño del código. Si tiene indicadores del compilador de tamaño de código, asegúrese de subirlos hasta la configuración más alta. Por lo general, compilar para el tamaño del código significa que su código se ejecutará más despacio ... pero puede que tenga que hacer eso.

+0

El uso de la optimización de alto nivel para el tamaño de IAR EW430 para un objetivo TI MSP430 redujo el tamaño del código compilado, pero esa no es la solución que elegí. –

+0

Al observar los indicadores del compilador que genera la opción, se pueden deshabilitar la alineación y el desenrollado de bucles. Esas son acciones ya anotadas en las respuestas actuales. –

+0

Parece que su problema ya está resuelto. Para mi propia curiosidad, en el compilador, al activar la optimización, ¿se inhabilitó el desenrollado en línea/bucle/etc? –

Cuestiones relacionadas