Compilador para optimización de declaraciones
¿Bajo qué condiciones optimiza el compilador las declaraciones For?
Los artículos escritos en este blog fueron creados con el objetivo de enseñar. Además, la información del artículo puede ser incorrecta, así que tenga cuidado.
Desde la perspectiva de la CPU, la instrucción JMP utilizada en la declaración For es: Debido a que la degradación del rendimiento ocurre cuando la canalización se interrumpe o falla la predicción de la rama, El compilador intenta no utilizar instrucciones JMP si se cumplen las condiciones.
- Cuando el número de iteraciones se confirma como una constante en el momento de la compilación.
Optimizable: for (int i = 0; i < 4; i++) (sabiendo que se repetirá 4 veces) Dificultad de optimización: for (int i = 0; i < n; i++) (si n es una variable determinada en tiempo de ejecución)
//código original
for (int i = 0; i < 3; i++) {
arr[i] = 0;
}
//Después de la optimización del compilador
arr[0] = 0;
arr[1] = 0;
arr[2] = 0;
Lenguaje ensamblador antes de la optimización.
.L2:
cmp DWORD PTR [rbp-4], 2 ;Comparar si i < 3
jg .L3 ;Si no se cumplen las condiciones, el bucle finaliza (salta a L3).
mov eax, DWORD PTR [rbp-4]
cdqe
mov DWORD PTR [rbp-32+rax*4], 0 ;arr[i] = 0 asignación
add DWORD PTR [rbp-4], 1 ; i++
jmp .L2 ;Salto incondicional hacia atrás al punto de inicio del bucle (.L2) (JMP)
.L3:
Lenguaje ensamblador optimizado
mov DWORD PTR [rdi], 0 ; arr[0] = 0
mov DWORD PTR [rdi+4], 0 ;arr[1] = 0 (+4 porque int tiene 4 bytes)
mov DWORD PTR [rdi+8], 0 ;arr[2] = 0 (+8 porque int tiene 4 bytes)
- Cuando el resultado final se puede calcular por adelantado en el momento de la compilación.
Cuando la lógica dentro del bucle no depende de entradas y salidas externas, el valor del resultado se reemplaza por una constante.
//código original
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
//Después de la optimización del compilador
//JMP ni siquiera realiza operaciones de suma en tiempo de ejecución.
return 5050;
Lenguaje ensamblador antes de la optimización.
.L5:
cmp DWORD PTR [rbp-4], 100 ;Comparar si i <= 100
jg .L6 ;O terminar el ciclo
mov eax, DWORD PTR [rbp-4]
add DWORD PTR [rbp-8], eax ; sum += i
add DWORD PTR [rbp-4], 1 ; i++
jmp .L5 ;Volver al inicio del bucle (.L5) (JMP)
.L6:
mov eax, DWORD PTR [rbp-8] ;Preparándose para devolver resultados
Lenguaje ensamblador después de la optimización.
mov eax, 5050 ;Inserte el valor del resultado calculado 5050 en el registro eax.
ret ;regresar inmediatamente
- Cuando la longitud del código es corta incluso con la expansión del bucle.
Expandir un bucle como el código 1 se llama expansión de bucle o desenrollado de bucle. Si la longitud del código se vuelve demasiado larga debido a la expansión del bucle, es un desperdicio de la memoria caché de instrucciones de la CPU. Si el tamaño del código dentro del bucle no es pequeño, Loop Unrolling no está optimizado.
- Cuando no hay un flujo de control complejo dentro del bucle.
Hay una declaración break, continue, goto o compleja if-else dentro del bucle, o Si se incluye una llamada a una función externa, el compilador no expande el bucle descuidadamente.
Si hay una función de E/S como printf, Dado que debemos proteger los efectos secundarios que se muestran en la pantalla, honestamente ejecutamos JMP y llamamos a la función sin omitir el bucle.
Además, la opción de optimización del compilador debe estar activada para intentar la optimización como se indica arriba.