simplemente el cambio de compiladores puede mejorar su rendimiento de C para el mismo código fuente por muchas veces. GCC no necesariamente ha mejorado para el rendimiento en los últimos años, para algunos programas, gcc 3.x produce un código mucho más estricto que 4.x. Cuando tenía acceso a las herramientas, el compilador ARM producía un código significativamente mejor que gcc. Tanto como 3 o 4 veces más rápido. LLVM ha alcanzado a GCC 4.x y sospecho que pasará a gcc en términos de rendimiento y uso general para la compilación cruzada de código incrustado. Pruebe diferentes versiones de gcc, 3.xy 4.x si está usando gcc. El compilador de Metaware y Arms Adt corrieron alrededor de gcc3.x, gcc3.x dará gcc4.xa ejecutar por su dinero con el código de brazo, para el código de pulgar gcc4.x es mejor y para thumb2 (que no se aplica a usted) gcc4.x también mejor. Recuerde que no he dicho una palabra acerca de cambiar una sola línea de código (todavía).
LLVM es capaz de optimización completa del programa, además de más perillas de ajuste infinitamente que gcc. A pesar de que el código generado (ver 27) solo está alcanzando al actual gcc 4.x en términos de rendimiento para los pocos programas que probé.Y no probé el n número factoral de combinaciones de optimización (optimizo en el paso de compilación, diferentes opciones para cada archivo, o combino dos archivos o tres archivos o todos los archivos y optimizo esos paquetes, mi teoría es no hacer optimizaciones en la C a la BC Pasos, enlace todos los BC juntos y luego haga una sola pasada de optimización en todo el programa, permita la optimización predeterminada cuando llc lo lleve al objetivo.
De la misma manera, simplemente conocer su compilador y las optimizaciones puede mejorar enormemente el rendimiento del código sin tener que cambiar nada de él. ¿Tiene un ARM11 arr compilando para arm11 o brazo genérico? Puede ganar entre un poco y un doce por ciento al decirle al compilador específicamente qué arquitectura/familia (por ejemplo, armv6) sobre el brazo genérico (ARM7) que a menudo se elige como la predeterminada. Sabiendo usar -O2 o -O3 si eres valiente.
A menudo no es el caso, pero cambiar al modo de pulgar puede mejorar el rendimiento para plataformas específicas. No se aplica a usted, pero el avance de gameboy es un ejemplo perfecto, cargado con buses de estado de espera no nulos de 16 bits. El pulgar tiene un puñado de sobrecarga porque requiere más instrucciones para hacer lo mismo, pero al aumentar los tiempos de recuperación, y aprovechando algunas de las características de lectura secuencial del código de gba, el código puede ejecutarse significativamente más rápido que el código de armado para el mismo código fuente
teniendo un brazo11 es probable que tenga un caché L1 y tal vez L2, ¿están encendidos? ¿Están configurados? ¿Tienes un mmu y tu memoria de uso intensivo está en caché? o ¿está ejecutando memoria de estado de espera cero y no necesita un caché y debe apagarlo? Además de no darse cuenta de que puede tomar el mismo código fuente y hacer que se ejecute mucho más rápido cambiando los compiladores u opciones, la gente a menudo no se da cuenta de que cuando usa un caché simplemente agrega un único hasta unos pocos puntos en su código de inicio (como un truco para ajustar el lugar donde el código aterriza en la memoria por una, dos, algunas palabras) puede cambiar la velocidad de ejecución de sus códigos hasta en un 10 a 20 por ciento. Donde esas lecturas de línea de caché tocan en las funciones/bucles muy utilizados hace una gran diferencia. Incluso guardando una línea de caché de lectura ajustando dónde cae el código es notable (por ejemplo, cortar de 3 a 2 o de 2 a 1).
Conociendo su arquitectura, tanto el procesador como su entorno de memoria es donde comenzaría la sintonización. La mayoría de las bibliotecas C si tiene un nivel alto suficiente para usar una (a menudo no uso una biblioteca C ya que funciono sin un sistema operativo y con recursos muy limitados) en su código C y algunas veces agrego algún ensamblador para crear rutinas de cuello de botella como memcpy. mucho mas rápido. Si sus programas funcionan en 32 direcciones alineadas o incluso mejores de 64 bits, y se ajusta incluso si eso significa usar un puñado de bytes más de memoria para que cada estructura/matriz/memcpy sea un múltiplo entero de 32 bits o 64 bits, verá mejoras notables (si su código usa estructuras o copia datos de otras maneras). Además de obtener sus estructuras (si las usa, ciertamente no con el código incrustado) alineadas con el tamaño, incluso si desperdicia memoria, alineando los elementos, considere usar enteros de 32 bits para cada elemento en lugar de bytes o medias palabras. Dependiendo de su sistema de memoria, esto puede ayudar (también puede doler). Al igual que con el ejemplo anterior de GBA, al buscar funciones específicas que, ya sea por perfil o intuición, usted sabe que no se están implementando de manera que aproveche su procesador o plataforma o bibliotecas, es posible que desee convertirlo en ensamblador desde cero o compilando desde C inicialmente. luego desmontar y afinar a mano. Memcpy es un buen ejemplo de que puede conocer el rendimiento de la memoria de su sistema y puede elegir crear su propia memcpy específicamente para datos alineados, copiando 64 o 128 o más bits por instrucción.
Del mismo modo, mezclar las variables globales y locales puede hacer una notable diferencia en el rendimiento. Tradicionalmente se les dice a las personas que nunca usen los globales, pero esto no es necesariamente cierto, depende de cuán profundamente incrustado y cuánto afinado y velocidad y otros factores le interesen. Este es un tema delicado y puede que me llame la atención, por lo que Lo dejaré así.
El compilador tiene que grabar y desalojar registros para realizar llamadas a funciones, y si usa variables locales puede ser necesario un marco de pila, por lo que las llamadas a funciones son costosas, pero al mismo tiempo, dependiendo del código dentro de un función que ahora ha crecido en tamaño evitando funciones, puede crear el problema que intentaba evitar, desalojando registros para reutilizarlos. Incluso una sola línea de código C puede hacer que la diferencia entre todas las variables en una función se ajuste en los registros a tener que comenzar a expulsar un grupo de registros. Para funciones o segmentos de código donde sabe que necesita algún rendimiento, compile y desmonte (y observe el uso del registro, la frecuencia con que recupera la memoria o escribe en la memoria). Puede y encontrará los lugares donde necesita tomar un ciclo bien utilizado y convertirlo en su propia función aunque la llamada a la función tenga una penalización porque al hacerlo el compilador puede optimizar mejor el ciclo y no desalojar/reutilizar los registros y obtiene un ganancia neta general. Incluso una sola instrucción adicional en un bucle que gira cientos de veces es un golpe de rendimiento cuantificable.
Afortunadamente, ya sabe que no compilar para la depuración, apague toda la compilación para las opciones de depuración. Es posible que ya sepa que la compilación de código para depuración que se ejecuta sin errores no significa que esté depurada, compilando errores y utilizando depuradores para ocultar errores dejándolos como bombas de tiempo en su código para su compilación final para el lanzamiento. Aprenda a compilar siempre para su lanzamiento y prueba con la versión de lanzamiento, tanto para el rendimiento como para encontrar errores en su código.
La mayoría de los conjuntos de instrucciones no tienen una función dividir. Evite usar divisiones o módulo en su código tanto como sea humanamente posible, son asesinos de rendimiento. Naturalmente, este no es el caso para los poderes de dos, para salvar al compilador y para evitar mentalmente las divisiones y los modulos que intentan usar los cambios y los ys. Las multiplicaciones son más fáciles y se encuentran con mayor frecuencia en los conjuntos de instrucciones, pero siguen siendo costosas. Este es un buen caso para escribir ensamblador para hacer sus multiplicaciones en lugar de dejar que lo haga la copiadora C. La multiplicación del brazo es 32bit * 32bit = 32 bit, así que para hacer cálculos precisos sin desbordamiento tiene que haber un código C adicional envuelto alrededor de la multiplicación, si ya sabes que no se desbordará, graba los registros para una llamada de función y haz la multiplicación en ensamblador (para el brazo).
Del mismo modo, la mayoría de los conjuntos de instrucciones no tienen una unidad de punto flotante, con la suya es posible que, aun así, evite la flotación si es posible. Si tiene que usar float, es una caja de pandora de problemas de rendimiento. La mayoría de las personas no ven los problemas de rendimiento con código tan simple como esto:
float a,b;
...
a = b * 7.0;
El resto del problema no es la comprensión de precisión de punto flotante y lo bueno o malo de las bibliotecas de C están tratando de obtener sus constantes en coma flotante formar. De nuevo, flotar es una larga discusión sobre los problemas de rendimiento.
Soy un producto de Michael Abrash (en realidad tengo una copia impresa del zen del lenguaje ensamblador) y la conclusión es el tiempo de su código. Propón una forma precisa de medir el tiempo del código, puedes pensar que sabes dónde están los cuellos de botella y puedes pensar que conoces tu arquitectura, pero probando diferentes cosas incluso si crees que están equivocadas, y al cronometrarlas puedes encontrar y eventualmente tener que descubre el error en tu forma de pensar Agregar nops para comenzar. Como un paso de ajuste final es un buen ejemplo de esto, todos los demás trabajos que ha realizado para el rendimiento se pueden borrar instantáneamente al no tener una buena alineación con el caché, esto también significa reorganizar las funciones dentro de su código fuente para que aterricen en diferentes lugares en la imagen binaria. He visto un aumento de 10 a 20 por ciento en el aumento y la disminución de la velocidad como resultado de las alineaciones de la línea de caché.
¿Hay un sistema operativo? ¿Qué es? ¿Qué compilador/cadena de herramientas estás usando? ¿Tienes GPIO de repuesto? ¿Tienes simulador de trabajo? _Embedded_ es un gran campo. – nategoose