Lo primero a considerar es qué es necesario optimizar. ¿El código necesita tener menos memoria de programa o debería ejecutarse más rápido? A veces, ambas cosas pueden lograrse reduciendo el número de instrucciones, sin embargo, esto depende del microcontrolador subyacente y del número de ciclos que toma cada instrucción.
Es completamente posible generar código más pequeño que requiera más ciclos para ejecutarse, especialmente si hay sucursales involucradas.
Para comenzar a experimentar, es importante aislar la porción de código más pequeña posible. Creé el caso de prueba simple a continuación que incluye la función de la pregunta y que es portátil entre diferentes familias de microcontroladores:
#include <stdint.h>
uint32_t MyFunction(uint32_t crc, uint8_t *buffer, uint16_t length) {
uint16_t i;
uint16_t j;
for (i = 0; i < length; i++) {
crc = crc ^ *buffer++;
for (j = 0; j < 8; j++) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc = crc >> 1;
}
}
return crc;
}
int main(int argc, char** argv) {
uint8_t data[] = "ABCDEF";
uint32_t ret = 0;
ret = MyFunction(ret, data, 6);
while(1);
}
Como se señala en los comentarios en la otra pregunta el valor de la variable i
nunca se usa directamente, solo se compara con length
. Por lo tanto podríamos reescribirlo de la siguiente manera:
uint32_t MyFunction(uint32_t crc, uint8_t *buffer, uint16_t length) {
uint16_t j;
while (length--) {
// .. do work ..
}
return crc;
}
Para fines de comparación, utilicé avr-gcc versión 4.7.2 (apuntando a atmega8) y XC8 1.21 de Microchip (apuntando a PIC 18F). Para XC8, habilité las optimizaciones PRO, para avr-gcc, los argumentos dados fueron: avr-gcc -g -c -Os -Wall -mmcu=atmega8 test.c -o test-Os
.
Tenga en cuenta que es importante verificar que ret
no esté optimizado porque el compilador piensa que no está en uso. Ambas variaciones en el código C generan el mismo ensamblaje desde avr-gcc. Sin embargo, XC8 no se da cuenta de la variable no utilizada, por lo tanto, el código que utiliza un bucle while(..)
se compila a 4 bytes más pequeños con 2 bytes de RAM guardados.
Como se señaló en un comentario sobre esta pregunta, también sería más eficiente hacer j
a uint8_t
, ya que la entrada nunca tendrá más de 8 bits de ancho. Las pruebas en XC8 muestran que hacer este cambio ahorra otros 8 bytes de espacio de programa y 1 byte de RAM. También reduce la salida generada con avr-gcc en 4 bytes y un byte de registro.
En conclusión, siempre vale la pena darle al compilador la mejor oportunidad de generar un buen código , en este caso, no utilizando variables innecesarias adicionales y utilizando la clase de almacenamiento más pequeña posible. Algunos compiladores optimizados se comportarán mejor que otros, pero ambos funcionan bien si el código C de entrada ha sido bien pensado.