Estaba leyendo las opciones de optimización para GCC cuando encontré la opción -funroll-all-loops .
Su descripción dice:
Desenrolle todos los bucles, incluso si su número de iteraciones es incierto cuando se ingresa al bucle. Esto generalmente hace que los programas se ejecuten más lentamente. '-funroll-all-loops' implica las mismas opciones que '-funroll-loops'
¿Cómo puede el compilador desenrollar un bucle si se desconoce el número de iteraciones en el momento de la compilación? ¿No necesita el compilador esta información para desenrollarlo? ¿Qué código C correspondiente genera y en qué contextos podría ser útil si normalmente hace que los programas se ejecuten más lentamente?
Aquí hay un código C que muestra cómo hacerlo:
int iterations = 100; int unrollValue = 8; while (iterations%unrollvalue) { // insert loop code here iterations--; } while (iterations) { // insert unrollValue copies of loop code here iterations-= unrollValue; }El compilador reemplazará el primer ciclo con un salto relativo, pero eso no es fácil de representar en C. Tenga en cuenta que desenrollar por una potencia de 2 le permite al compilador usar una máscara en lugar de la (costosa) operación de división.
¿En qué contextos podría ser útil esto si normalmente hace que los programas se ejecuten más lentamente?
Bueno, están asumiendo que si eliges esta opción sabes lo que estás haciendo, si no, no deberías usar esta opción.
qué va a hacer gcc, bueno, usé este programa de muestra:
#include <stdio.h> void f(int j ) { for( int k = 0; k < j; ++k ) { printf( "%d\n", k ) ; } }y lo probé con Godbolt y genera una tabla de salto basada en el número de iteraciones restantes (véalo en vivo ):
cmpl $1, %ebp movl $1, %ebx je .L1 testl %r12d, %r12d je .L27 cmpl $1, %r12d je .L28 cmpl $2, %r12d je .L29 cmpl $3, %r12d je .L30 cmpl $4, %r12d je .L31 cmpl $5, %r12d je .L32 cmpl $6, %r12d je .L33Puede hacer algo como:
while(n >= 8){ foo(); foo(); foo(); foo(); foo(); foo(); foo(); foo(); n -= 8; } while(n > 0){ foo(); n--; }Por supuesto, Duff's Device ahorraría tener que escribir el segundo ciclo.
¿Por que hacerlo? Eso depende del usuario. Si foo() gasta más de unos pocos ciclos, o si el ciclo original toma menos del 5% del tiempo total del reloj de pared, o si n es típicamente pequeño, probablemente no valga la pena.