El plegado constante y la propagación constante son optimizaciones de compilador relacionadas que utilizan muchos compiladores modernos . [1] Una forma avanzada de propagación constante conocida como propagación constante condicional dispersa puede propagar constantes con mayor precisión y eliminar simultáneamente el código muerto .
El plegado de constantes es el proceso de reconocer y evaluar expresiones constantes en tiempo de compilación en lugar de calcularlas en tiempo de ejecución. Los términos en expresiones constantes suelen ser literales simples, como el literal entero 2
, pero también pueden ser variables cuyos valores se conocen en tiempo de compilación. Considere la declaración:
yo = 320 * 200 * 32 ;
La mayoría de los compiladores no generarían dos instrucciones de multiplicación y un almacenamiento para esta instrucción. En cambio, identifican construcciones como estas y sustituyen los valores calculados en el momento de la compilación (en este caso, 2,048,000
).
El plegado de constantes puede hacer uso de identidades aritméticas. Si x
es numérico, el valor de 0 * x
es cero incluso si el compilador no conoce el valor de x
(tenga en cuenta que esto no es válido para los flotantes IEEE, ya que x
podrían ser Infinity o NaN . Aún así, algunos entornos que favorecen el rendimiento, como los sombreadores GLSL, permiten esto para constantes, lo que ocasionalmente puede causar errores).
El plegado constante puede aplicarse a más que solo números. La concatenación de cadenas literales y cadenas constantes puede ser objeto de plegado constante. El código como "abc" + "def"
puede reemplazarse por "abcdef"
.
Al implementar un compilador cruzado , se debe tener cuidado de garantizar que el comportamiento de las operaciones aritméticas en la arquitectura del host coincida con el de la arquitectura de destino, ya que de lo contrario, habilitar el plegado constante cambiará el comportamiento del programa. Esto es de particular importancia en el caso de las operaciones de punto flotante , cuya implementación precisa puede variar ampliamente.
La propagación de constantes es el proceso de sustitución de los valores de constantes conocidas en expresiones en tiempo de compilación. Dichas constantes incluyen las definidas anteriormente, así como las funciones intrínsecas aplicadas a valores constantes. Considere el siguiente pseudocódigo:
int x = 14 ; int y = 7 - x / 2 ; devuelve y * ( 28 / x + 2 );
Propagando x obtenemos:
int x = 14 ; int y = 7 - 14 / 2 ; devuelve y * ( 28 / 14 + 2 );
Al continuar con la propagación se obtiene lo siguiente (que probablemente se optimizaría aún más mediante la eliminación del código muerto de x e y).
int x = 14 ; int y = 0 ; devuelve 0 ;
La propagación de constantes se implementa en compiladores utilizando resultados de análisis de definición de alcance . Si todas las definiciones de alcance de la variable tienen la misma asignación que asigna la misma constante a la variable, entonces la variable tiene un valor constante y se puede reemplazar con la constante.
La propagación constante también puede hacer que las ramas condicionales se simplifiquen a una o más declaraciones incondicionales, cuando la expresión condicional puede evaluarse como verdadera o falsa en tiempo de compilación para determinar el único resultado posible.
El plegado y la propagación constantes se suelen utilizar juntos para lograr muchas simplificaciones y reducciones, y su aplicación iterativa intercalada continúa hasta que cesan esos efectos.
Considere este pseudocódigo no optimizado que devuelve un número desconocido pendiente de análisis:
int a = 30 ; int b = 9 - ( a / 5 ); int c = b * 4 ; si ( c > 10 ) { c = c - 10 ; } devuelve c * ( 60 / a );
Aplicando una propagación constante una vez, seguida de un plegado constante, se obtiene:
int a = 30 ; int b = 3 ; int c = b * 4 ; si ( c > 10 ) { c = c - 10 ; } devuelve c * 2 ;
Repitiendo ambos pasos dos veces se obtiene:
int a = 30 ; int b = 3 ; int c = 12 ; si ( verdadero ) { c = 2 ; } devuelve c * 2 ;
Después de haber reemplazado todos los usos de variables a
y con constantes, la eliminación de código muertob
del compilador se aplica a esas variables, quedando:
int c = 12 ; si ( verdadero ) { c = 2 ; } devuelve c * 2 ;
(Las construcciones booleanas varían entre lenguajes y compiladores, pero sus detalles (como el estado, el origen y la representación de verdadero ) no afectan estos principios de optimización).
La propagación constante tradicional no produce más optimización; no reestructura los programas.
Sin embargo, una optimización similar, la propagación constante condicional dispersa , va más allá al seleccionar la rama condicional adecuada [2] y eliminar la prueba condicional de siempre verdadero. De este modo, la variable c
se vuelve redundante y solo queda una operación sobre una constante:
devolver 4 ;
Si ese pseudocódigo constituye un cuerpo de función, el compilador sabe que la función se evalúa como una constante entera 4
, lo que permite reemplazar las llamadas a la función con 4
, y aumenta aún más la eficiencia del programa.
propagación constante O plegamiento constante.