Tengo este fragmento de código para probar el comportamiento de la declaración de cambio frente a los punteros de función:
#include <stdio.h> #include <time.h> int mySum(int startingValue) { return startingValue += 1; } int mySub(int startingValue) { return startingValue -= 1; } int main() { time_t rawtime; struct tm * timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); printf("USING SWITCH\n"); printf("Start time: %s", asctime(timeinfo)); int startingValue = 25; int currentOperation = 0; // Using switch for (long long i = 0; i < 10000000000; i++) { switch (currentOperation) { case 1: startingValue = mySum(startingValue); break; case 0: startingValue = mySub(startingValue); break; } if (currentOperation) currentOperation = 0; else currentOperation = 1; } printf("Result is: %d\n", startingValue); time(&rawtime); timeinfo = localtime(&rawtime); printf("End time: %s", asctime(timeinfo)); printf("\n\n\n"); time(&rawtime); timeinfo = localtime(&rawtime); printf("USING FUNCTION POINTERS\n"); printf("Start time: %s", asctime(timeinfo)); startingValue = 25; currentOperation = 0; // Using function pointers int (*mySwitchOfFunctionPointers[2])(int x); mySwitchOfFunctionPointers[0] = &mySub; mySwitchOfFunctionPointers[1] = &mySum; for (long long i = 0; i < 10000000000; i++) { startingValue = (*mySwitchOfFunctionPointers[currentOperation])(startingValue); if (currentOperation) currentOperation = 0; else currentOperation = 1; } printf("Result is: %d\n", startingValue); time(&rawtime); timeinfo = localtime(&rawtime); printf("End time: %s", asctime(timeinfo)); return 0; }El resultado es este, por lo que el código se ejecuta instantáneamente con la declaración de cambio, pero necesita muchos segundos con punteros de función:
USING SWITCH Start time: Mon Oct 25 18:04:06 2021 Result is: 25 End time: Mon Oct 25 18:04:06 2021 USING FUNCTION POINTERS Start time: Mon Oct 25 18:04:06 2021 Result is: 25 End time: Mon Oct 25 18:04:34 2021Compilado con
gcc -c main.c -o test.o -O3 -Wall -Wno-unused -std=c99 gcc test.o -o test¿Por qué el segundo enfoque es más lento que el primero? ¿Hay algo mal en el código? ¿Alguna idea?
El compilador puede alinear el interruptor y luego el optimizador es lo suficientemente inteligente como para darse cuenta de que todo su bucle no funciona, por lo que lo optimiza. No hace esto con los punteros de función. Ver https://godbolt.org/z/qxxoof5cn
Si cambia su código a algo que el compilador no puede optimizar tan trivialmente, obtendrá resultados muy diferentes. Por ejemplo:
int mySum(int startingValue) { return startingValue += rand(); } int mySub(int startingValue) { return startingValue -= rand(); }El argumento de optimización del compilador (-O3) es responsable de este comportamiento.
El compilador puede optimizar el primero for -Loop con el switch dentro, porque tiene toda la información necesaria sobre este código.
Desafortunadamente, el compilador no puede optimizar el código con punteros de función. Dado que el compilador no sabe a qué funciones se llama.
Si elimina el argumento -O3 al compilar, verá que los punteros de función y las declaraciones switch-case necesitan tiempos similares.