fbpx
Wikipedia

Compilador optimizador

En ciencias de la computación, un compilador optimizador es un compilador que trata de minimizar ciertos atributos de un programa informático con el fin de aumentar la eficiencia y rendimiento.[1]​ Las optimizaciones del compilador se aplican generalmente mediante una secuencia de transformaciones de optimización, algoritmos que transforman un programa para producir otro con una salida semánticamente equivalente pero optimizado.[2]

Generalmente hay varios aspectos que se desean optimizar:[3]

  1. Optimización temporal: Reducir el tiempo de ejecución del programa.
  2. Optimización espacial: Reducir la cantidad de espacio en memoria que ocupa el programa en ejecución.
  3. Reducir el tamaño del programa.
  4. Minimizar la potencia consumida por un programa (debido a las computadoras portátiles).
Etapas de compilación

La optimización se realiza después de la generación de código de todo el programa o de un elemento ejecutable del programa (función, procedimiento, etc), por ende es dependiente del contexto.[4]​ La condición que ha de cumplir es que el código optimizado se ha de comportar igual que el código de partida, excepto por ser más rápido o ocupar menos espacio.[3]

Se ha demostrado que algunos problemas de optimización de código son NP-completo, o incluso indecidibles.[5][6]​ En la práctica, factores como la voluntad del programador que debe esperar a que el compilador complete sus tareas, imponen límites superiores en las optimizaciones que las que una simple implementación del compilador puede proporcionar (la optimización es un proceso muy intensivo que generalmente necesita mucho procesamiento y memoria para llevarse a cabo). En el pasado, las limitaciones de memoria de las computadoras también eran un factor importante en la limitación de las optimizaciones que se podían realizar. Debido a todos estos factores, la optimización rara vez produce una salida de forma óptima (valga la redundancia), y el hecho de que una optimización pueda impedir el rendimiento en algunos casos, hace que, sean métodos heurísticos para mejorar el uso de los recursos en los programas típicos.[5]

La optimización es el campo donde se hace más investigación en los compiladores hoy en día. Las tareas del front-end (exploración, análisis sintáctico, análisis léxico, análisis semántico) son bien conocidas y, sin optimizar, la generación de código es relativamente sencilla. La optimización, por otra parte, aún es un campo que no se ha terminado de desarrollar de forma completa.[5]

Etapas de un compilador optimizador

Un compilador optimizador tipo consta de tres fases cuando realiza el proceso de optimización:[6]

  1. Análisis de control del flujo: construcción de grafos de control del flujo útiles para juntar información para realizar análisis. Se identifican los bucles.
  2. Análisis de flujo de datos: proceso de recolectar la información acerca de como se usan las variables en el programa. Asociadas a cada bloque básico y estructuras de control (if/else, bucles, etc).
  3. Transformaciones: a partir de la información recolectada, se hacen las transformaciones convenientes para optimizar el código

Tipos de optimización

Las técnicas utilizadas en la optimización se puede dividir entre varios tipos que pueden afectar cualquier propiedad, desde una sola instrucción, a todo el programa.

Técnicas globales, locales e interprocedurales

En términos generales, las técnicas de ámbito local son más fáciles de aplicar que las globales pero que resultan en menores ganancias.

Las técnicas locales se aplican en el bloque básico[7]​ (Un bloque básico es un fragmento de código que tiene una única entrada y salida, y cuyas instrucciones se ejecutan secuencialmente. Si se ejecuta una instrucción del bloque se ejecutan todas en un orden conocido en tiempo de compilación[8]​) dado que los bloques básicos no tienen control de flujo, estas optimizaciones necesitan muy poco análisis (lo que ahorra tiempo y reduce los requisitos de almacenamiento), pero esto también significa que ninguna información se conserva a través de saltos, y las optimizaciones al ser tan puntuales no tienen gran impacto sobre el programa.[9]

Hay varios tipos de optimizaciones locales entre los que se encuentran principalmente:[10]

  1. Plegamiento de constantes (Constant floding)
  2. Propagación de constantes (Constat propagation)
  3. Reducción de potencia (Strength reduction)
  4. Reducción de subexpresiones comunes (Common subexpression elimination)

Las técnicas globales, también llamadas intraprocedurales, se realizan en una función o procedimiento entero.[11]​ Estas técnicas son más costosas, lo que requiere grandes cantidades de tiempo de compilación, pero puede proporcionar un gran aumento en el rendimiento ya que posee mucho más conocimiento del programa que las locales.[12]

Por último las interprocedurales son las que analizan comportamiento entre procedimientos llegando a analizar el código completo.[13]​ Lógicamente estas técnicas son las que más tiempo de compilación necesitan debido a la gran cantidad y costo de los análisis, pero las mejoras pueden ser notables.[14]

Dependientes e independientes de máquina

Muchas optimizaciones que operan en conceptos abstractos de programación (bucles, estructuras, objetos) son independientes de la máquina prevista por el compilador, pero muchas de las optimizaciones más eficaces son las que mejor explotan las particularidades de la plataforma de destino.[15]

Dependientes e independientes del lenguaje de programación

La mayoría de los lenguajes de alto nivel comparten estructuras comunes de programación y abstracciones: decisión (if, switch, case), bucle (for, while, repeat.. until, do.. while), y la encapsulación (estructuras, objetos). Dado esto, varias técnicas de optimización similares pueden ser utilizadas en todos los lenguajes. Sin embargo, algunas características de los mismos hacen algunos tipos de optimizaciones difícil. Por ejemplo, la existencia de los punteros en C y C++ hace que sea difícil optimizar accesos de arreglos (ver análisis de alias).[16]

Otras optimizaciones

Algunos tipos de optimizaciones son muy comunes debido a sus características y es importante nombrarlos individualmente.

Optimizaciones de ciclos

En prácticamente cualquier libro/publicación sobre optimización de código se habla del principio de 90/10 que asegura que se utiliza el 90% del tiempo de ejecución de un programa en ejecutar el 10% del código del mismo.[17]​ Esto es debido a los bucles y la reutilización de código, por ende, la optimización de código dentro de los bucles suele dar resultados muy convenientes teniendo en cuenta la poca cantidad de código que se optimiza.[18]

Existen muchas técnicas de optimización de bucles que se explicaran más adelante.

Optimizaciones peephole

Las optimizaciones peephole son transformaciones simples que dan un beneficio suficiente como para ser implementadas en cualquier compilador.[19]​ Básicamente, examina el código assembler por secuencias pequeñas y las reemplaza por secuencias más corta y que ejecuten más rápido si es posible.[19]

Optimización del código máquina

Este tipo de optimización analiza la imagen ejecutable de la tarea del programa después de que todo el código máquina ejecutable ha sido enlazado. Algunas de las técnicas que se pueden aplicar tienen un alcance más limitado, tales como la compresión de macro (que ahorra espacio por el colapso de secuencias comunes de instrucciones), son más eficaces cuando la imagen ejecutable de la tarea completa está disponible para el análisis.[20]

Factores que afectan a la optimización

Hay varios factores externos que afectan al rendimiento de un software a tener en cuenta:[21]

La propia máquina

Muchas de las decisiones acerca de qué optimizaciones pueden y deben hacerse dependen de las características de la máquina de destino. A veces es posible parametrizar algunos de estos factores dependientes de la máquina, de modo que una sola pieza de código del compilador se puede utilizar para optimizar diferentes máquinas simplemente mediante la alteración de los parámetros de la descripción de la máquina. GCC es un compilador que es un ejemplo de este enfoque.[22]

La arquitectura de la CPU objetivo

El número de registros de CPU: Hasta cierto punto, cuanto más registros, más fácil es optimizar el rendimiento. Las variables locales pueden ser asignadas en los registros y no en la pila. Resultados temporales/intermedios se puede dejar en registros sin escribir y leer desde la memoria.

RISC vs. CISC

El conjunto de instrucciones CISC a menudo tienen longitud de instrucciones variables, tienen un mayor número de instrucciones posibles que se pueden utilizar, y cada instrucción podría tomar distintas cantidades de tiempo. El conjunto de instrucciones RISC intenta limitar la variabilidad de cada una de ellas: conjuntos de instrucciones son generalmente de longitud constante, con pocas excepciones, por lo general hay menos combinaciones de los registros y en las operaciones de memoria, además la tasa de emisión de instrucciones (el número de instrucciones completadas por período de tiempo, normalmente un múltiplo entero del ciclo de reloj) es generalmente constante en los casos en que la latencia de memoria no es un factor. Puede haber varias formas de llevar a cabo una tarea determinada, con CISC suelen ofrecer más alternativas que RISC. Los compiladores tienen que conocer los costos relativos entre las diversas instrucciones y elegir la mejor secuencia de instrucciones (ver selección de instrucción).[23]

Número de unidades funcionales

Algunas CPUs tienen varias ALU y FPU. Esto les permite ejecutar múltiples instrucciones simultáneamente. Puede haber restricciones en la cual las instrucciones se pueden emparejar con los que otras instrucciones ("emparejamiento" es la ejecución simultánea de dos o más instrucciones), y sobre que unidad funcional puede ejecutar que instrucción. También tienen problemas similares a los conflictos de los pipelines. De forma similar, las instrucciones deben ser programadas de modo que las diversas unidades funcionales están completamente alimentadas con las instrucciones a ejecutar.[24]

Pipeline

Un pipeline es esencialmente una CPU dividida en una línea de montaje. Permite el uso de piezas de la CPU para diferentes instrucciones mediante la ruptura de la ejecución de instrucciones en varias etapas: decodificación de instrucciones, decodificación de direcciones, fetch de memoria, fetch de registro, calcular, guardar registros, etc. Una instrucción podría estar en la etapa de almacenar en registros, mientras que otra podría estar en la etapa de fetching. Los conflictos de pipeline se producen cuando una instrucción en una etapa del pipeline depende del resultado de otra instrucción por delante de él en el pipeline, pero todavía no ha finalizado. Los conflictos pueden llevar a paradas del pipeline: donde la CPU consume ciclos de espera para resolver un conflicto. Los compiladores pueden programar o reordenar, las instrucciones para que las paradas del pipeline se produzcan con menos frecuencia.[25]

La arquitectura de la máquina

Tamaño y tipo de caché

Técnicas tales como expansión en línea y desenrollamiento de bucle pueden aumentar el tamaño del código generado y reducir la localidad código. El programa puede disminuir drásticamente si una sección muy utilizada de código (como los bucles internos en varios algoritmos) de repente no puede entrar en la memoria caché. Además, las memorias caché que no son totalmente asociativas tienen mayores probabilidades de colisiones caché incluso en una memoria caché de vacantes.[26]

Velocidad de transferencia de la caché/memoria

Estos datos dan al compilador una indicación de la penalidad por fallos de caché. Esto se usa principalmente en aplicaciones especializadas.

Uso previsto del código generado

Depuración

Mientras se escribe una aplicación, un programador volverá a compilar y poner a prueba muchas veces, por eso la compilación debe ser rápida. Esta es una razón por la cual la mayoría de las optimizaciones se evita deliberadamente durante la fases de prueba/depuración. Además, el código del programa suele intercalare (ver animación de programa), utilizando un depurador simbólico, y las transformaciones de optimización, a menudo reordena el código. Esto puede hacer que sea difícil relacionar el código de salida con los números de línea en el código fuente original y puede confundir tanto a las herramientas de depuración como a los programadores que las utilizan.

Uso de propósitos generales

El software empaquetado, muy a menudo se espera para ser ejecutado en una variedad de máquinas y CPUs que pueden compartir el mismo conjunto de instrucciones, pero tienen diferentes tiempos de caché, o características de memoria. Así, el código no puede ser sintonizado a cualquier CPU particular, o puede ser sintonizado a trabajar mejor en la CPU más popular y aun así funciona aceptablemente bien en otras CPU.

Uso de propósitos especiales

Si el software es compilado para ser utilizado en una o unas pocas máquinas muy similares, con características conocidas, el compilador puede optimizar en gran medida el código generado para esas máquinas específicas (si estas opciones están disponibles). Importantes casos especiales incluyen un código diseñado para los procesadores vectoriales y paralelo, para lo cual compiladores especiales de paralelización son empleados.

Sistemas embebidos

Estos son un caso común de propósito especial de uso. El software embebido puede estar bien sintonizado en una CPU exacta y el tamaño de la memoria. Además, el coste del sistema o la fiabilidad puede ser más importante que la velocidad del código. Así, por ejemplo, los compiladores de software embebido suelen ofrecer opciones que reducen el tamaño del código a expensas de la velocidad, porque la memoria es el costo principal de un sistema embebido. El código de sincronización puede necesitar ser predecible, en lugar de tan rápido como sea posible, el almacenamiento en caché del código puede ser desactivado, junto con las optimizaciones del compilador que lo requieran.

Temas comunes

En gran medida, las técnicas de optimización del compilador tienen los siguientes temas, que a veces en conflicto.

Optimizar el caso más común

El caso común puede tener propiedades únicas que permiten un camino rápido a expensas de un camino lento. Si el camino rápido se toma más a menudo, el resultado es un mejor rendimiento general.

Evitar redundancias

Reutilizar los resultados que ya están calculados y almacenarlos para su uso posterior, en vez de recalcular ellos aumenta significativamente el rendimiento general.

Menos código

Eliminar cálculos innecesarios y los valores intermedios. Menos trabajo para la CPU, la memoria caché y la memoria por lo general resulta en una ejecución más rápida. Por otra parte, en los sistemas embebidos, menos código trae un costo de producto más bajo.

Menos saltos mediante el uso de código de línea recta

Menos código complicado. Los saltos (condicionales o incondicionales) interfieren con fetching previo de instrucciones, lo que ralentiza el código.[27]​ El uso de expansión en línea o desenrrollamiento de bucle puede reducir de ramificación, a costa de aumentar el tamaño del archivo binario por la longitud del código repetido. Esto tiende a combinar varios elementos básicos en uno solo.

Localidad

El código y datos que son accesibles estrechamente juntos en el tiempo deben ser colocados cerca en la memoria para aumentar la cercanía de referencias.

Utilizar la jerarquía de memoria

Los accesos a memoria son cada vez más caro para cada nivel de la jerarquía de memoria, por lo que se debe colocar los elementos más utilizados en los primeros registros, luego los cachés de memoria principal y, a continuación, antes de entrar en el disco.[28]

Paralelismo

Reordenar las operaciones para permitir que múltiples cálculos se produzcan en paralelo, ya sea en la instrucción, la memoria, o a nivel de hilo.

Uso de información más precisa

Cuanto más precisa sea la información que el compilador tiene, mejor se puede emplear cualquiera o todas estas técnicas de optimización.

Métricas

La información recopilada durante una ejecución de prueba se puede utilizar en una optimización guiada por perfiles. La información obtenida en tiempo de ejecución (a ser posible con un mínimo de overhead) se puede utilizar por un compilador JIT para mejorar la optimización dinámica.

Reducción de potencias

Reducir las operaciones complejas, difíciles o costosos con otras más simples. Por ejemplo, reemplazar la división por una constante con la multiplicación por su recíproco, o realizar un análisis de variables inductivas para sustituir a la multiplicación por un índice de bucle con adición.

Técnicas específicas

Optimización de bucles

Se pueden realizar muchas optimizaciones en los bucles.[29]​ No todas se pueden llevar a cabo a la vez.

Análisis de variables inductivas

En términos generales, si una variable en un bucle es una función lineal simple de la variable de índice, tal como j:= 4 * i + 1, puede ser actualizado adecuadamente cada vez que la variable de bucle se cambia. Esto es reducción de potencia, y también puede permitir que las definiciones de la variable de índice se convierta en código muerto. Esta información también es útil para la eliminación de los límites de comprobación y el análisis de la dependencia, entre otras cosas.[30][31]

Fisión de bucle

Fisión de bucle es una técnica de optimización del compilador que intenta romper un bucle en múltiples bucles en el rango del mismo índice pero cada uno teniendo solo una parte del cuerpo del bucle original. El objetivo es derribar el cuerpo del bucle grande en otros más pequeños para lograr una mejor localidad de datos. Es la acción inversa a la fusión de bucle.[32]

Fusión de bucle

Fusión de bucle es una técnica para la optimización del compilador y transformación de bucles, que sustituye a múltiples bucles con uno sola. Es la acción inversa a la fisión bucle.[33]

Inversión de bucles

Inversión de bucles es una técnica en la optimización del compilador, sobre todo en la transformación de bucles. Esta técnica cambia un bucle while estándar, en un do/while con un if condicional, reduciendo el número de saltos entre los dos, para los casos en que se ejecuta el bucle. Si lo hace, duplica la comprobación de la condición (aumentando el tamaño del código), pero es más eficiente porque los saltos suelen causar paradas del pipeline. Además, si la condición inicial es conocida en tiempo de compilación y se sabe que es libre de efectos secundarios, el if protector puede ser omitido.[34]

Intercambio de bucle

Intercambio de bucle, una técnica de optimización del compilador, es el proceso de intercambio del orden de dos variables de iteración. Uno de los propósitos principales de intercambio de bucle es mejorar el rendimiento de la caché para el acceso a elementos de un array. Los fallos de caché ocurren si los elementos accedidos de un array contiguo dentro del bucle provienen de una línea de caché diferente. Intercambio de bucle puede ayudar a prevenir esto. La eficacia del intercambio de bucle depende y debe ser considerado teniendo en cuenta el modelo de caché utilizado por el hardware subyacente y el modelo de arrays utilizado por el compilador.[35]

Loop-invariant code motion

Si una cantidad se calcula dentro de un bucle en cada iteración, y su valor es el mismo para cada iteración, se puede mejorar enormemente la eficiencia llevándola fuera del bucle y calcular su valor solo una vez antes de que el bucle comience. Esto es particularmente importante con las expresiones de cálculo de direcciones generadas por bucles sobre arrays. Para la aplicación correcta, esta técnica debe ser utilizado con la técnica inversión de bucles, porque no todo el código es seguro para ser colocado fuera del bucle.[36]

Optimización nido de bucle

Algunos algoritmos generalizados, como la multiplicación de arrays tienen un comportamiento muy pobre en cache y accesos excesivos de memoria. La optimización nido de bucle aumenta el número de coincidencias de memoria caché mediante la realización de la operación a través de pequeños bloques y mediante el uso de un bucle intercambio (por eso el nombre de nido).[37]

Reversión de bucle

Reversión de bucle invierte el orden en el que se asignan valores a la variable del índice. Esta es una optimización sutil que puede ayudar a eliminar dependencias, permitiendo así otras optimizaciones.[38]

Desenrollamiento de bucle

Desenrollamiento de bucle, es una técnica para optimizar partes de los programas de ordenador. La idea es ahorrar tiempo al reducir el número de instrucciones generales que el equipo tiene que ejecutar en un bucle, y así mejorar la tasa de aciertos de caché y reducir la ramificación. Para lograr esto, las instrucciones que se llaman en múltiples iteraciones del bucle se combinan en una sola iteración. Esto acelerará el programa si las instrucciones generales del bucle perjudican el rendimiento de forma significativa.[39]

División de bucle

División de bucle, también conocido como loop peeling, es una técnica de optimización del compilador. Se trata de simplificar un bucle o eliminar dependencias dividiéndola en múltiples lazos que tienen los mismos cuerpos pero iteran sobre diferentes partes contiguas del rango del índice.[40]

Loop unswitching

Loop unswitching es una técnica de optimización del compilador. Se mueve un condicional dentro de un bucle a fuera del mismo mediante la duplicación del cuerpo del bucle, y colocando una versión del mismo en el interior de cada una de las cláusulas if y else. Esto puede mejorar la paralelización del bucle. Dado que los procesadores modernos pueden operar rápido en vectores esto aumenta la velocidad.[41]

Fragmentado de bucle

Fragmentado de bucle, también conocido como loop tiling, es una optimización de bucles utilizada por los compiladores para que la ejecución de ciertos tipos de bucles sea más eficiente. La idea es particionar el espacio de un bucle de iteración en trozos más pequeños o bloques, a fin de ayudar a asegurar que los datos utilizados en un bucle permanece en la caché hasta que se vuelve a utilizar. La división de espacio de un bucle de iteración conduce a la partición de gran variedad en bloques más pequeños, así se pueden cargar pequeños trozos de un array en la cache, rehusar elementos de la misma y se eliminan los requerimientos de tamaño de esta.[42]

Software pipelining

El bucle se reestructura de tal manera que el trabajo realizado en una iteración se divide en varias partes y hecho en varias iteraciones. En un bucle estrecho esta técnica oculta la latencia entre la carga y el uso de valores.

Paralelización automática

Un bucle se convierte en código multi-hilo o vectorizado (o incluso los dos) con el fin de utilizar varios procesadores simultáneamente en una memoria compartida de multiprocesador (SMP) de la máquina, incluyendo núcleos en múltiples máquinas.

Optimización del flujo de datos

Las optimizaciones del flujo de datos, basadas en el análisis de flujo de datos, dependen principalmente de cómo ciertas propiedades de los datos se propagan por las aristas del grafo de control del flujo. Algunos de estos incluyen:

Eliminación de la subexpresión común

Eliminar operaciones que son redundantes en sub-expresiones logra una reducción de código. Así, muchas de las veces a una variable se le asigna un valor y se reutiliza varias veces, ya que dicha variable es común en otras operaciones. Pro ejemplo: X=A*SIN(Y)+(SIN(Y)**2), realmente puede ser vista como: t=SIN(Y); X=A*t+(t**2).[43]

Plegamiento de constantes

De las técnicas de optimización la más trivial es la que se refiere a la compactación del código, es decir que algunas expresiones pueden ser simplificadas mediante la evaluación del compilador como puede ser "8+3+C+d", en la declaración el compilador la dejará como "11+C+d", claramente se observa que el compilador reemplaza los números 8 y 3 por su resultante, por supuesto se aplica casi para cualquier tipo de operación.[43]

Propagación de constantes

Similar el plegamiento ésta constante se sustituye y es reescrita de una forma implícita. Así por ejemplo, cuando hacemos la evaluación de: c = 4; valor = c/2 éste puede ser mejor usado como valor = 4/2, y éste a su vez ser plegado como se mencionó anteriormente.[43]

Reconocimiento y eliminación de variables inductivas

Ver el apartado de análisis de variable de inducción más arriba.

Análisis de punteros y clasificación de alias

En presencia de punteros, es difícil hacer alguna optimización en absoluto, ya que potencialmente cualquier variable se ha de cambiar cuando una ubicación de memoria se la asigna. Mediante la especificación de que punteros pueden tener un alias a una variables, punteros no relacionados pueden ser ignorados.[44]

Eliminación de almacenamiento muerto

Eliminar las asignaciones a variables que no son posteriormente leídas, ya sea por la vida útil de la variable o debido a que se sobrescribirá el primer valor antes de ser usado.

Optimizaciones basadas en SSA

Estas optimizaciones se pretende hacer después de transformar el programa en una static single assignment form, en el que se asigna cada variable en un solo lugar. Aunque alguna función sin SSA, son más eficaces que con SSA. Muchas optimizaciones que figuran en otras secciones también se benefician sin cambios especiales, tales como asignación de registros.

Valor de numeración global

El valor de numeración global elimina la redundancia mediante la construcción de un gráfico de valores del programa y, a continuación determinar qué valores se calculan con expresiones equivalentes. El valor de numeración global es capaz de identificar alguna redundancia que la eliminación de subexpresiones comunes no puede, y viceversa.[45]

Propagación de constantes condicional escasa

Efectivamente equivalente a la realización iterativa de propagación de constantes, el plegamiento constante, y la eliminación de código muerto, pero es mucho más eficiente. Esta optimización simbólicamente ejecuta el programa, al mismo tiempo la propagación de valores constantes y elimina partes del grafo de control de flujo que son inalcanzables.[46][47]

Optimización del código generado

Asignación de registros

El buen uso de los registros es la característica más importante de un código eficiente.Las instrucciones que involucran registros son más rápidasque las que involucran operandos en memoria. Se utiliza la información recolectada en análisis de variables vivas Y se usan técnicas de coloreo de grafos para saber que variable asignar a cada registro.[48]

Selección de instrucciones

La mayoría de las arquitecturas, y en particular las CISC tienen muchos modos de direccionamiento, ofrecen varias formas de llevar a cabo una operación en particular y el uso de secuencias completamente diferentes de instrucciones. La naturaleza del conjunto de instrucciones de la máquina objeto determina la dificultad de la selección de instrucciones. Es importante que el conjunto de instrucciones sea uniforme y completo. Si la máquina objeto no apoya cada tipo de datos de una manera uniforme, entonces cada excepción a la regla general exige un tratamiento especial. Las velocidades de las instrucciones y las expresiones particulares de la máquina son otros factores importantes. Para cada tipo de proposición de tres direcciones, se puede diseñar un esqueleto de código que perfila el código objeto que ha de generarse para esa construcción.[49]

Planificación de instrucciones

La planificación de instrucciones es una optimización importante para los procesadores modernos segmentados, lo que evita paradas o burbujas en el pipeline por instrucciones de agrupamiento sin dependencias juntas, teniendo cuidado de preservar la semántica original.[50][51]

Rematerialización

La rematerialización vuelve a calcular un valor en lugar de utilizar un acceso a memoria. Esto se realiza en conjunto con la asignación de registros para evitar derrames.[52][53]

Factorización de código

Si varias secuencias de código son idénticos, o se puede parametrizar o reordenado para ser idénticas, pueden ser reemplazados con llamadas a una subrutina compartida. Esto a menudo puede compartir código para subrutinas de configuración y algunas veces de cola de recursión.[54]

Trampolines

Muchas CPUs tienen pequeñas subrutinas de llamadas a instrucciones para acceder a niveles bajos de memoria. Un compilador puede ahorrar espacio mediante el uso de estas llamadas pequeñas en el cuerpo principal del código. Cambiar instrucciones en la memoria baja puede acceder a las rutinas en cualquier dirección. Esto multiplica el ahorro de espacio de la factorización de código.[54]

Reordenamiento de cálculos

Basado en la Programación de enteros, los compiladores de reestructuración mejoran la localidad de datos y exponen más paralelismo con cálculos reordenados. Optimizando el espacio los compiladores pueden reordenar el código para alargar las secuencias que pueden ser un factor en subrutinas.[55]

Otras optimizaciones

Reducción de potencias

Se busca sustituir operaciones costosas por otras más simples. Por ejemplo: sustituir productos por sumas, evitar la operación append, sustituir productos entre variables inductivas e invariantes de bucle por sumas.[56]

Eliminación de los límites de comprobación

Muchas lenguas, por ejemplo Java, hace cumplir los límites de comprobación de todos los accesos a un array. Esto es un grave cuello de botella en ciertas aplicaciones tales como código científico. La eliminación de los límites de comprobación permite al compilador quitar con seguridad la comprobación de límites en muchas situaciones donde se puede determinar que el índice debe estar dentro de los límites válidos, por ejemplo, si se trata de una variable de bucle simple.[57]

Optimización del desplazamiento de salto

Elige el desplazamiento de la rama más corta que llega a destino.[58]

Reordenamiento de bloques de código

El reordenamiento de bloques de código altera el orden de los bloques básicos en un programa con el fin de reducir los saltos condicionales y mejorar la cercanía de referencias.[59]

Eliminación de código muerto

La eliminación de código muerto incluye quitar del código fuente cualquier tipo de código muerto, código inalcanzable y código redundante.[60]

Expansión en línea o Expansión de macro

La expansión en línea es una técnica de optimización del compilador que reduce la sobrecarga de una llamada a una función, simplemente no haciendo la llamada, por el contrario, el compilador reescribe eficazmente la función en el programa como si la definición de la función llamada se insertó en cada sitio que fue invocada.[61]

Jump threading

En este paso, consecutivos saltos condicionales predichos total o parcialmente en la misma condición se fusionan.[62]​ Por ejemplo:

if (c) { foo; } if (c) { bar;} if (c) { foo; bar;} 
if (c) { foo; } if (!c) { bar;} if (c) { foo; } else { bar;} 

Compresión de macros

Una optimización de espacio que reconoce secuencias comunes de código, crea subprogramas ("macros de código") que contienen el código común, y reemplaza las ocurrencias de las secuencias de códigos comunes con las llamadas a los subprogramas correspondientes.[63]​ Esto se realiza más eficazmente como una optimización del código máquina, cuando todo el código está presente. La técnica fue utilizada por primera vez para ahorrar espacio en un flujo de bytes interpretativo utilizado en una implementación de Macro Spitbol en microcomputadoras.[64]​ El problema de determinar un conjunto óptimo de macros que minimiza el espacio requerido por un segmento de código dado se sabe que es NP-completo,[63]​ pero las heurísticas eficientes alcanzan casi resultados óptimos.[65]

Reducción de la altura de la pila

Reorganizar árbol de expresión para minimizar los recursos necesarios para la evaluación de la expresión.[66]

Test reordering

Si tenemos dos pruebas que son la condición para algo, lo primero que se hace son las pruebas más simples y solo después las pruebas complejas. Esta técnica complementa la evaluación perezosa pero solo se pueden utilizar cuando las pruebas no son dependientes uno del otro.

Problemas con la optimización

Temprano en la historia de los compiladores, las optimizaciones del compilador no eran tan buenos como las escritos a mano. Dado que las tecnologías han mejorado, los compiladores buenos a menudo puede generar un mejor código que los programadores humanos y una buena optimización del código objeto puede mejorar el código altamente optimizado a mano aún más. Para arquitecturas de CPU RISC, y más aún para el hardware de VLIW, un compilador optimizador es clave para obtener un código eficiente, porque los conjuntos de instrucciones RISC son tan compactos que es difícil para un ser humano programar manualmente o combinar pequeñas instrucciones para obtener resultados eficientes. En efecto, estas arquitecturas fueron diseñados para apoyarse en los programadores de compiladores para un rendimiento adecuado.

Sin embargo, los compiladores optimizadores no son de ninguna manera perfectos.[67]​ No hay manera de que un compilador puede garantizar que, para todo código fuente de un programa, se genera la salida equivalente más rápida o más pequeña; tal compilador es imposible, ya que resolvería el problema de la parada.

Esto puede ser demostrado mediante la consideración de una llamada a una función, foo(). Esta función devuelve nada y no tiene efectos secundarios (no E/S, no modifica las variables globales ni estructuras de datos, etc.) El programa equivalente más rápido posible sería simplemente eliminar la llamada a la función. Sin embargo, si la función foo() en realidad no regresa, entonces el programa con la llamada a foo() sería diferente del programa sin la llamada, el compilador de optimización tendrá entonces que determinar esto resolviendo el problema de la parada.

Además, hay un número de otras cuestiones prácticas con la tecnología de un compilador optimizador:

  • La optimización de los compiladores se centran en la mejoras de desempeño relativamente poco profundas y no suelen mejorar la complejidad algorítmica de una solución. Por ejemplo, un compilador no va a cambiar una aplicación de tipo burbuja a un quicksort para mejorar el rendimiento.
  • Los compiladores suelen tener que soportar una variedad de objetivos en conflicto, como el coste de la velocidad de compilación ejecución y calidad del código generado.
  • Un compilador típicamente solo se ocupa de una parte de un programa a la vez, a menudo el código contenido dentro de un único archivo o módulo, el resultado es que no puede tener en cuenta información contextual que solo se puede conseguir mediante el procesamiento de los otros archivos.
  • La sobrecarga de optimización del compilador: Cualquier trabajo adicional requiere tiempo, todo el programa de optimización es mucho tiempo para programas grandes.
  • La interacción a menudo compleja entre las fases de optimización hace que sea difícil encontrar una secuencia óptima en la que ejecutar las diferentes fases de optimización.

Véase también

Referencias

  1. José M. Maria Carrasco. «La optimización: una mejora en la optimización de programas». Consultado el 15 de enero de 2013. 
  2. Cooper, Keith D., and Torczon, Linda, Engineering a Compiler, Morgan Kaufmann, 2004, ISBN 1-55860-699-8
  3. «Optimización de compiladores». Consultado el 15 de enero de 2013. 
  4. «Optimización de código». Consultado el 15 de enero de 2013. 
  5. Maggie Johnson (4 de agosto de 2008). «Code Optimization» (en inglés). Consultado el 15 de enero de 2013. 
  6. «Compilador optimizador». Consultado el 15 de enero de 2013. 
  7. Cooper, Keith D., and Torczon, Linda, Engineering a Compiler, Morgan Kaufmann, 2004, ISBN 1-55860-699-8 page 404
  8. «Optimización de código». Consultado el 15 de enero de 2013. 
  9. IBM. «Optimizaciones locales». Consultado el 15 de enero de 2013. 
  10. «Compiladores, optimización». Consultado el 15 de enero de 2013. 
  11. Cooper, Keith D., and Torczon, Linda, Engineering a Compiler, Morgan Kaufmann, 2004, ISBN 1-55860-699-8 page 407
  12. IBM. «Global optimizations» (en inglés). Consultado el 15 de enero de 2013. 
  13. Universidad de Virginia (8 de abril de 2008). «Interprocedural Optimizations» (en inglés). Consultado el 15 de enero de 2013. 
  14. Michael Burke y Linda Torczon. «Interprocedural Optimization: Eliminating Unnecessary Recompilation» (en inglés). Consultado el 15 de enero de 2013. 
  15. «OPTIMIZACIÓN DE CÓDIGO». Consultado el 15 de enero de 2013. 
  16. Peter Fritzson, Christoph Kessler (2011). [Vector (informática) «Code optimization»] |url= incorrecta (ayuda) (en inglés). Consultado el 15 de enero de 2013. 
  17. «Optimizing your programs» (en inglés). Consultado el 15 de enero de 2013. 
  18. «Optimizacion de compiladores». Consultado el 15 de enero de 2013. 
  19. «Optimizing compilers» (en inglés). Consultado el 15 de enero de 2013. 
  20. Clinton F. Goss (junio de 1986). Machine Code Optimization - Improving Executable Object Code. New York University. Consultado el 24-Mar-2011. 
  21. Chattopadhiay, Santanu (2005). Compiler Desing. New Delhi: Prentice Hall. p. 225. ISBN 81-203-2726-X |isbn= incorrecto (ayuda). 
  22. Free Software Foundation. «Options That Control Optimization» (en inglés). Consultado el 16 de enero de 2013. 
  23. Farhat Masood. «RISC AND CISC Computer Architecture» (en inglés). Consultado el 16 de enero de 2013. 
  24. «Superescalar processors» (en inglés). Consultado el 16 de enero de 2013. 
  25. . Archivado desde el original el 5 de septiembre de 2011. Consultado el 16 de enero de 2013. 
  26. «Introduction of Cache Memory» (en inglés). Consultado el 16 de enero de 2013. 
  27. «Pipeline». Consultado el 16 de enero de 2013. 
  28. «Jerarquía de memorias». Marzo de 2011. Consultado el 16 de enero de 2013. 
  29. Utpal Barnerjee. «Loop paralelization» (en inglés). Consultado el 16 de enero de 2013. 
  30. «Induction variables» (en inglés). Consultado el 16 de enero de 2013. 
  31. Sébastian Pop. «Analysis of induction variables using chains of recurrences: extensions» (en francés). Consultado el 16 de enero de 2013. 
  32. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  33. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  34. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  35. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  36. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  37. (en inglés). Archivado desde el original el 3 de febrero de 2013. Consultado el 16 de enero de 2013. 
  38. «Transfomation loop» (en inglés). Consultado el 16 de enero de 2013. 
  39. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  40. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  41. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  42. (en inglés). Archivado desde el original el 23 de mayo de 2012. Consultado el 16 de enero de 2013. 
  43. . Archivado desde el original el 2 de enero de 2010. Consultado el 16 de enero de 2013. 
  44. Mike Acton (1 de junio de 2006). «Understanding Strict Aliasing» (en inglés). Consultado el 16 de enero de 2013. 
  45. Alpern, Bowen, Wegman, Mark N., and Zadeck, F. Kenneth (enero de 1988). "Detecting Equality of Variables in Programs". San Diego, CA, USA: Conference Record of the Fifteenth Annual ACM Symposium on Principles of Programming Languages (POPL), ACM Press. pp. 1-11. 
  46. Wegman, Mark N. and Zadeck, F. Kenneth. "Constant Propagation with Conditional Branches." ACM Transactions on Programming Languages and Systems, 13(2), April 1991, pages 181-210.
  47. Click, Clifford and Cooper, Keith. "Combining Analyses, Combining Optimizations", ACM Transactions on Programming Languages and Systems, 17(2), March 1995, pages 181-196
  48. «Code optimization» (en inglés). Consultado el 16 de enero de 2013. 
  49. «COMPILADORES». Consultado el 16 de enero de 2013. 
  50. Peter Lee (2009). «Introduction to Instruction Scheduling» (en inglés). Consultado el 16 de enero de 2013. 
  51. «Instruction Scheduling» (en inglés). Consultado el 16 de enero de 2013. 
  52. P. Briggs, K. D. Cooper, and L. Torczon. (julio de 1992). Proceedings of the SIGPLAN 92 Conference on Programming Language Design and Implementation. SIGPLAN Notices 27(7). pp. 311-321. 
  53. Mukta Punjani. . Archivado desde el original el 28 de abril de 2005. Consultado el 16 de enero de 2013. 
  54. Cx51 Compiler Manual, version 09.2001, p155, Keil Software Inc.
  55. Adams, L. (1986), Reordering computations for parallel execution. Commun. appl. numer. methods, 2: 263–271. doi: 10.1002/cnm.1630020307
  56. «Optimización de código». Consultado el 16 de enero de 2013. 
  57. Xan Gregg (22 de enero de 2006). (en inglés). Archivado desde el original el 13 de marzo de 2013. Consultado el 16 de enero de 2013. 
  58. Yu Chen, Fuxin Zhang (Junio de 2007). Code reordering on limited branch offset. NY, USA: ACM Transactions on Architecture and Code Optimization (TACO). doi:10.1145/1250727.1250730. 
  59. Free software fundation (5 de mayo de 2000). «Basic Block Reordering» (en inglés). Consultado el 16 de enero de 2013. 
  60. «Code Optimizations: Partial Dead Code Elimination» (en inglés). 13 de abril de 2009. Consultado el 16 de enero de 2013. 
  61. «Inline Expansion» (en inglés). Consultado el 16 de enero de 2013. 
  62. «Options That Control Optimization» (en inglés). Consultado el 16 de enero de 2013. 
  63. Clinton F. Goss (June de 1986). «Machine Code Optimization - Improving Executable Object Code». New York University. Consultado el 24-Mar-2011. 
  64. Robert B. K. Dewar; Martin Charles Golumbic; Clinton F. Goss (October de 1979). MICRO SPITBOL. Computer Science Department Technical Report. No. 11. Courant Institute of Mathematical Sciences. 
  65. Martin Charles Golumbic; Robert B. K. Dewar; Clinton F. Goss (1980). «Macro Substitutions in MICRO SPITBOL - a Combinatorial Analysis». Proc. 11th Southeastern Conference on Combinatorics, Graph Theory and Computing, Congressus Numerantium, Utilitas Math., Winnipeg, Canada 29: 485-495. 
  66. «Advanced Computer Architecture» (en inglés). Consultado el 16 de enero de 2013. 
  67. Kent Wilken. (en inglés). Archivado desde el original el 30 de enero de 2013. Consultado el 16 de enero de 2013. 

Bibliografía

  • A. Aho, R. Sethi, J.D. Ullman, Compilers: Principles, Techniques, and Tools, Reading, MA: Addison-Wesley, 1986.
  • J.P. Bennett, Introduction to Compiling Techniques. Berkshire, England: McGraw-Hill, 1990.
  • R. Mak, Writing Compilers and Interpreters. New York, NY: Wiley, 1991.
  • S. Muchnick, Advanced Compiler Design and Implementation. San Francisco, CA: Morgan Kaufmann, 1997.
  • A. Pyster, Compiler Design and Construction. Reinhold, 1988.

Enlaces externos

  • Advanced compiler optimizations for supercomputers
  • Ejemplo, se lleva a cabo varias de la optimizaciones nombradas en este artículo a un código
  •   Datos: Q1325106

compilador, optimizador, ciencias, computación, compilador, optimizador, compilador, trata, minimizar, ciertos, atributos, programa, informático, aumentar, eficiencia, rendimiento, optimizaciones, compilador, aplican, generalmente, mediante, secuencia, transfo. En ciencias de la computacion un compilador optimizador es un compilador que trata de minimizar ciertos atributos de un programa informatico con el fin de aumentar la eficiencia y rendimiento 1 Las optimizaciones del compilador se aplican generalmente mediante una secuencia de transformaciones de optimizacion algoritmos que transforman un programa para producir otro con una salida semanticamente equivalente pero optimizado 2 Generalmente hay varios aspectos que se desean optimizar 3 Optimizacion temporal Reducir el tiempo de ejecucion del programa Optimizacion espacial Reducir la cantidad de espacio en memoria que ocupa el programa en ejecucion Reducir el tamano del programa Minimizar la potencia consumida por un programa debido a las computadoras portatiles Etapas de compilacion La optimizacion se realiza despues de la generacion de codigo de todo el programa o de un elemento ejecutable del programa funcion procedimiento etc por ende es dependiente del contexto 4 La condicion que ha de cumplir es que el codigo optimizado se ha de comportar igual que el codigo de partida excepto por ser mas rapido o ocupar menos espacio 3 Se ha demostrado que algunos problemas de optimizacion de codigo son NP completo o incluso indecidibles 5 6 En la practica factores como la voluntad del programador que debe esperar a que el compilador complete sus tareas imponen limites superiores en las optimizaciones que las que una simple implementacion del compilador puede proporcionar la optimizacion es un proceso muy intensivo que generalmente necesita mucho procesamiento y memoria para llevarse a cabo En el pasado las limitaciones de memoria de las computadoras tambien eran un factor importante en la limitacion de las optimizaciones que se podian realizar Debido a todos estos factores la optimizacion rara vez produce una salida de forma optima valga la redundancia y el hecho de que una optimizacion pueda impedir el rendimiento en algunos casos hace que sean metodos heuristicos para mejorar el uso de los recursos en los programas tipicos 5 La optimizacion es el campo donde se hace mas investigacion en los compiladores hoy en dia Las tareas del front end exploracion analisis sintactico analisis lexico analisis semantico son bien conocidas y sin optimizar la generacion de codigo es relativamente sencilla La optimizacion por otra parte aun es un campo que no se ha terminado de desarrollar de forma completa 5 Indice 1 Etapas de un compilador optimizador 2 Tipos de optimizacion 2 1 Tecnicas globales locales e interprocedurales 2 2 Dependientes e independientes de maquina 2 3 Dependientes e independientes del lenguaje de programacion 2 4 Otras optimizaciones 2 4 1 Optimizaciones de ciclos 2 4 2 Optimizaciones peephole 2 4 3 Optimizacion del codigo maquina 3 Factores que afectan a la optimizacion 3 1 La propia maquina 3 2 La arquitectura de la CPU objetivo 3 2 1 RISC vs CISC 3 2 2 Numero de unidades funcionales 3 2 3 Pipeline 3 3 La arquitectura de la maquina 3 3 1 Tamano y tipo de cache 3 3 2 Velocidad de transferencia de la cache memoria 3 4 Uso previsto del codigo generado 3 4 1 Depuracion 3 4 2 Uso de propositos generales 3 4 3 Uso de propositos especiales 3 4 4 Sistemas embebidos 4 Temas comunes 4 1 Optimizar el caso mas comun 4 2 Evitar redundancias 4 3 Menos codigo 4 4 Menos saltos mediante el uso de codigo de linea recta 4 5 Localidad 4 6 Utilizar la jerarquia de memoria 4 7 Paralelismo 4 8 Uso de informacion mas precisa 4 9 Metricas 4 10 Reduccion de potencias 5 Tecnicas especificas 5 1 Optimizacion de bucles 5 1 1 Analisis de variables inductivas 5 1 2 Fision de bucle 5 1 3 Fusion de bucle 5 1 4 Inversion de bucles 5 1 5 Intercambio de bucle 5 1 6 Loop invariant code motion 5 1 7 Optimizacion nido de bucle 5 1 8 Reversion de bucle 5 1 9 Desenrollamiento de bucle 5 1 10 Division de bucle 5 1 11 Loop unswitching 5 1 12 Fragmentado de bucle 5 1 13 Software pipelining 5 1 14 Paralelizacion automatica 5 2 Optimizacion del flujo de datos 5 2 1 Eliminacion de la subexpresion comun 5 2 2 Plegamiento de constantes 5 2 3 Propagacion de constantes 5 2 4 Reconocimiento y eliminacion de variables inductivas 5 2 5 Analisis de punteros y clasificacion de alias 5 2 6 Eliminacion de almacenamiento muerto 5 3 Optimizaciones basadas en SSA 5 3 1 Valor de numeracion global 5 3 2 Propagacion de constantes condicional escasa 5 4 Optimizacion del codigo generado 5 4 1 Asignacion de registros 5 4 2 Seleccion de instrucciones 5 4 3 Planificacion de instrucciones 5 4 4 Rematerializacion 5 4 5 Factorizacion de codigo 5 4 6 Trampolines 5 4 7 Reordenamiento de calculos 5 5 Otras optimizaciones 5 5 1 Reduccion de potencias 5 5 2 Eliminacion de los limites de comprobacion 5 5 3 Optimizacion del desplazamiento de salto 5 5 4 Reordenamiento de bloques de codigo 5 5 5 Eliminacion de codigo muerto 5 5 6 Expansion en linea o Expansion de macro 5 5 7 Jump threading 5 5 8 Compresion de macros 5 5 9 Reduccion de la altura de la pila 5 5 10 Test reordering 6 Problemas con la optimizacion 7 Vease tambien 8 Referencias 9 Bibliografia 10 Enlaces externosEtapas de un compilador optimizador EditarUn compilador optimizador tipo consta de tres fases cuando realiza el proceso de optimizacion 6 Analisis de control del flujo construccion de grafos de control del flujo utiles para juntar informacion para realizar analisis Se identifican los bucles Analisis de flujo de datos proceso de recolectar la informacion acerca de como se usan las variables en el programa Asociadas a cada bloque basico y estructuras de control if else bucles etc Transformaciones a partir de la informacion recolectada se hacen las transformaciones convenientes para optimizar el codigoTipos de optimizacion EditarLas tecnicas utilizadas en la optimizacion se puede dividir entre varios tipos que pueden afectar cualquier propiedad desde una sola instruccion a todo el programa Tecnicas globales locales e interprocedurales Editar En terminos generales las tecnicas de ambito local son mas faciles de aplicar que las globales pero que resultan en menores ganancias Las tecnicas locales se aplican en el bloque basico 7 Un bloque basico es un fragmento de codigo que tiene una unica entrada y salida y cuyas instrucciones se ejecutan secuencialmente Si se ejecuta una instruccion del bloque se ejecutan todas en un orden conocido en tiempo de compilacion 8 dado que los bloques basicos no tienen control de flujo estas optimizaciones necesitan muy poco analisis lo que ahorra tiempo y reduce los requisitos de almacenamiento pero esto tambien significa que ninguna informacion se conserva a traves de saltos y las optimizaciones al ser tan puntuales no tienen gran impacto sobre el programa 9 Hay varios tipos de optimizaciones locales entre los que se encuentran principalmente 10 Plegamiento de constantes Constant floding Propagacion de constantes Constat propagation Reduccion de potencia Strength reduction Reduccion de subexpresiones comunes Common subexpression elimination Las tecnicas globales tambien llamadas intraprocedurales se realizan en una funcion o procedimiento entero 11 Estas tecnicas son mas costosas lo que requiere grandes cantidades de tiempo de compilacion pero puede proporcionar un gran aumento en el rendimiento ya que posee mucho mas conocimiento del programa que las locales 12 Por ultimo las interprocedurales son las que analizan comportamiento entre procedimientos llegando a analizar el codigo completo 13 Logicamente estas tecnicas son las que mas tiempo de compilacion necesitan debido a la gran cantidad y costo de los analisis pero las mejoras pueden ser notables 14 Dependientes e independientes de maquina Editar Muchas optimizaciones que operan en conceptos abstractos de programacion bucles estructuras objetos son independientes de la maquina prevista por el compilador pero muchas de las optimizaciones mas eficaces son las que mejor explotan las particularidades de la plataforma de destino 15 Dependientes e independientes del lenguaje de programacion Editar La mayoria de los lenguajes de alto nivel comparten estructuras comunes de programacion y abstracciones decision if switch case bucle for while repeat until do while y la encapsulacion estructuras objetos Dado esto varias tecnicas de optimizacion similares pueden ser utilizadas en todos los lenguajes Sin embargo algunas caracteristicas de los mismos hacen algunos tipos de optimizaciones dificil Por ejemplo la existencia de los punteros en C y C hace que sea dificil optimizar accesos de arreglos ver analisis de alias 16 Otras optimizaciones Editar Algunos tipos de optimizaciones son muy comunes debido a sus caracteristicas y es importante nombrarlos individualmente Optimizaciones de ciclos Editar Vease tambien Optimizacion de bucles En practicamente cualquier libro publicacion sobre optimizacion de codigo se habla del principio de 90 10 que asegura que se utiliza el 90 del tiempo de ejecucion de un programa en ejecutar el 10 del codigo del mismo 17 Esto es debido a los bucles y la reutilizacion de codigo por ende la optimizacion de codigo dentro de los bucles suele dar resultados muy convenientes teniendo en cuenta la poca cantidad de codigo que se optimiza 18 Existen muchas tecnicas de optimizacion de bucles que se explicaran mas adelante Optimizaciones peephole Editar Vease tambien Optimizacion peephole Las optimizaciones peephole son transformaciones simples que dan un beneficio suficiente como para ser implementadas en cualquier compilador 19 Basicamente examina el codigo assembler por secuencias pequenas y las reemplaza por secuencias mas corta y que ejecuten mas rapido si es posible 19 Optimizacion del codigo maquina Editar Este tipo de optimizacion analiza la imagen ejecutable de la tarea del programa despues de que todo el codigo maquina ejecutable ha sido enlazado Algunas de las tecnicas que se pueden aplicar tienen un alcance mas limitado tales como la compresion de macro que ahorra espacio por el colapso de secuencias comunes de instrucciones son mas eficaces cuando la imagen ejecutable de la tarea completa esta disponible para el analisis 20 Factores que afectan a la optimizacion EditarHay varios factores externos que afectan al rendimiento de un software a tener en cuenta 21 La propia maquina Editar Muchas de las decisiones acerca de que optimizaciones pueden y deben hacerse dependen de las caracteristicas de la maquina de destino A veces es posible parametrizar algunos de estos factores dependientes de la maquina de modo que una sola pieza de codigo del compilador se puede utilizar para optimizar diferentes maquinas simplemente mediante la alteracion de los parametros de la descripcion de la maquina GCC es un compilador que es un ejemplo de este enfoque 22 La arquitectura de la CPU objetivo Editar El numero de registros de CPU Hasta cierto punto cuanto mas registros mas facil es optimizar el rendimiento Las variables locales pueden ser asignadas en los registros y no en la pila Resultados temporales intermedios se puede dejar en registros sin escribir y leer desde la memoria RISC vs CISC Editar Vease tambien Complex instruction set computing Vease tambien RISC El conjunto de instrucciones CISC a menudo tienen longitud de instrucciones variables tienen un mayor numero de instrucciones posibles que se pueden utilizar y cada instruccion podria tomar distintas cantidades de tiempo El conjunto de instrucciones RISC intenta limitar la variabilidad de cada una de ellas conjuntos de instrucciones son generalmente de longitud constante con pocas excepciones por lo general hay menos combinaciones de los registros y en las operaciones de memoria ademas la tasa de emision de instrucciones el numero de instrucciones completadas por periodo de tiempo normalmente un multiplo entero del ciclo de reloj es generalmente constante en los casos en que la latencia de memoria no es un factor Puede haber varias formas de llevar a cabo una tarea determinada con CISC suelen ofrecer mas alternativas que RISC Los compiladores tienen que conocer los costos relativos entre las diversas instrucciones y elegir la mejor secuencia de instrucciones ver seleccion de instruccion 23 Numero de unidades funcionales Editar Vease tambien Superescalar Algunas CPUs tienen varias ALU y FPU Esto les permite ejecutar multiples instrucciones simultaneamente Puede haber restricciones en la cual las instrucciones se pueden emparejar con los que otras instrucciones emparejamiento es la ejecucion simultanea de dos o mas instrucciones y sobre que unidad funcional puede ejecutar que instruccion Tambien tienen problemas similares a los conflictos de los pipelines De forma similar las instrucciones deben ser programadas de modo que las diversas unidades funcionales estan completamente alimentadas con las instrucciones a ejecutar 24 Pipeline Editar Vease tambien Arquitectura en pipeline informatica Un pipeline es esencialmente una CPU dividida en una linea de montaje Permite el uso de piezas de la CPU para diferentes instrucciones mediante la ruptura de la ejecucion de instrucciones en varias etapas decodificacion de instrucciones decodificacion de direcciones fetch de memoria fetch de registro calcular guardar registros etc Una instruccion podria estar en la etapa de almacenar en registros mientras que otra podria estar en la etapa de fetching Los conflictos de pipeline se producen cuando una instruccion en una etapa del pipeline depende del resultado de otra instruccion por delante de el en el pipeline pero todavia no ha finalizado Los conflictos pueden llevar a paradas del pipeline donde la CPU consume ciclos de espera para resolver un conflicto Los compiladores pueden programar o reordenar las instrucciones para que las paradas del pipeline se produzcan con menos frecuencia 25 La arquitectura de la maquina Editar Tamano y tipo de cache Editar Vease tambien Cache informatica Tecnicas tales como expansion en linea y desenrollamiento de bucle pueden aumentar el tamano del codigo generado y reducir la localidad codigo El programa puede disminuir drasticamente si una seccion muy utilizada de codigo como los bucles internos en varios algoritmos de repente no puede entrar en la memoria cache Ademas las memorias cache que no son totalmente asociativas tienen mayores probabilidades de colisiones cache incluso en una memoria cache de vacantes 26 Velocidad de transferencia de la cache memoria Editar Estos datos dan al compilador una indicacion de la penalidad por fallos de cache Esto se usa principalmente en aplicaciones especializadas Uso previsto del codigo generado Editar Depuracion Editar Vease tambien Depuracion de programas Mientras se escribe una aplicacion un programador volvera a compilar y poner a prueba muchas veces por eso la compilacion debe ser rapida Esta es una razon por la cual la mayoria de las optimizaciones se evita deliberadamente durante la fases de prueba depuracion Ademas el codigo del programa suele intercalare ver animacion de programa utilizando un depurador simbolico y las transformaciones de optimizacion a menudo reordena el codigo Esto puede hacer que sea dificil relacionar el codigo de salida con los numeros de linea en el codigo fuente original y puede confundir tanto a las herramientas de depuracion como a los programadores que las utilizan Uso de propositos generales Editar El software empaquetado muy a menudo se espera para ser ejecutado en una variedad de maquinas y CPUs que pueden compartir el mismo conjunto de instrucciones pero tienen diferentes tiempos de cache o caracteristicas de memoria Asi el codigo no puede ser sintonizado a cualquier CPU particular o puede ser sintonizado a trabajar mejor en la CPU mas popular y aun asi funciona aceptablemente bien en otras CPU Uso de propositos especiales Editar Si el software es compilado para ser utilizado en una o unas pocas maquinas muy similares con caracteristicas conocidas el compilador puede optimizar en gran medida el codigo generado para esas maquinas especificas si estas opciones estan disponibles Importantes casos especiales incluyen un codigo disenado para los procesadores vectoriales y paralelo para lo cual compiladores especiales de paralelizacion son empleados Sistemas embebidos Editar Vease tambien Sistema embebido Estos son un caso comun de proposito especial de uso El software embebido puede estar bien sintonizado en una CPU exacta y el tamano de la memoria Ademas el coste del sistema o la fiabilidad puede ser mas importante que la velocidad del codigo Asi por ejemplo los compiladores de software embebido suelen ofrecer opciones que reducen el tamano del codigo a expensas de la velocidad porque la memoria es el costo principal de un sistema embebido El codigo de sincronizacion puede necesitar ser predecible en lugar de tan rapido como sea posible el almacenamiento en cache del codigo puede ser desactivado junto con las optimizaciones del compilador que lo requieran Temas comunes EditarEn gran medida las tecnicas de optimizacion del compilador tienen los siguientes temas que a veces en conflicto Optimizar el caso mas comun Editar El caso comun puede tener propiedades unicas que permiten un camino rapido a expensas de un camino lento Si el camino rapido se toma mas a menudo el resultado es un mejor rendimiento general Evitar redundancias Editar Reutilizar los resultados que ya estan calculados y almacenarlos para su uso posterior en vez de recalcular ellos aumenta significativamente el rendimiento general Menos codigo Editar Eliminar calculos innecesarios y los valores intermedios Menos trabajo para la CPU la memoria cache y la memoria por lo general resulta en una ejecucion mas rapida Por otra parte en los sistemas embebidos menos codigo trae un costo de producto mas bajo Menos saltos mediante el uso de codigo de linea recta Editar Menos codigo complicado Los saltos condicionales o incondicionales interfieren con fetching previo de instrucciones lo que ralentiza el codigo 27 El uso de expansion en linea o desenrrollamiento de bucle puede reducir de ramificacion a costa de aumentar el tamano del archivo binario por la longitud del codigo repetido Esto tiende a combinar varios elementos basicos en uno solo Localidad Editar El codigo y datos que son accesibles estrechamente juntos en el tiempo deben ser colocados cerca en la memoria para aumentar la cercania de referencias Utilizar la jerarquia de memoria Editar Los accesos a memoria son cada vez mas caro para cada nivel de la jerarquia de memoria por lo que se debe colocar los elementos mas utilizados en los primeros registros luego los caches de memoria principal y a continuacion antes de entrar en el disco 28 Paralelismo Editar Vease tambien Paralelismo Informatica Reordenar las operaciones para permitir que multiples calculos se produzcan en paralelo ya sea en la instruccion la memoria o a nivel de hilo Uso de informacion mas precisa Editar Cuanto mas precisa sea la informacion que el compilador tiene mejor se puede emplear cualquiera o todas estas tecnicas de optimizacion Metricas Editar La informacion recopilada durante una ejecucion de prueba se puede utilizar en una optimizacion guiada por perfiles La informacion obtenida en tiempo de ejecucion a ser posible con un minimo de overhead se puede utilizar por un compilador JIT para mejorar la optimizacion dinamica Reduccion de potencias Editar Reducir las operaciones complejas dificiles o costosos con otras mas simples Por ejemplo reemplazar la division por una constante con la multiplicacion por su reciproco o realizar un analisis de variables inductivas para sustituir a la multiplicacion por un indice de bucle con adicion Tecnicas especificas EditarOptimizacion de bucles Editar Articulo principal Optimizacion de bucles Se pueden realizar muchas optimizaciones en los bucles 29 No todas se pueden llevar a cabo a la vez Analisis de variables inductivas Editar Vease tambien Analisis de variables inductivas En terminos generales si una variable en un bucle es una funcion lineal simple de la variable de indice tal como j 4 i 1 puede ser actualizado adecuadamente cada vez que la variable de bucle se cambia Esto es reduccion de potencia y tambien puede permitir que las definiciones de la variable de indice se convierta en codigo muerto Esta informacion tambien es util para la eliminacion de los limites de comprobacion y el analisis de la dependencia entre otras cosas 30 31 Fision de bucle Editar Vease tambien Fision de bucle Fision de bucle es una tecnica de optimizacion del compilador que intenta romper un bucle en multiples bucles en el rango del mismo indice pero cada uno teniendo solo una parte del cuerpo del bucle original El objetivo es derribar el cuerpo del bucle grande en otros mas pequenos para lograr una mejor localidad de datos Es la accion inversa a la fusion de bucle 32 Fusion de bucle Editar Vease tambien Fusion de bucle Fusion de bucle es una tecnica para la optimizacion del compilador y transformacion de bucles que sustituye a multiples bucles con uno sola Es la accion inversa a la fision bucle 33 Inversion de bucles Editar Vease tambien Inversion de bucles Inversion de bucles es una tecnica en la optimizacion del compilador sobre todo en la transformacion de bucles Esta tecnica cambia un bucle while estandar en un do while con un if condicional reduciendo el numero de saltos entre los dos para los casos en que se ejecuta el bucle Si lo hace duplica la comprobacion de la condicion aumentando el tamano del codigo pero es mas eficiente porque los saltos suelen causar paradas del pipeline Ademas si la condicion inicial es conocida en tiempo de compilacion y se sabe que es libre de efectos secundarios el if protector puede ser omitido 34 Intercambio de bucle Editar Vease tambien Intercambio de bucle Intercambio de bucle una tecnica de optimizacion del compilador es el proceso de intercambio del orden de dos variables de iteracion Uno de los propositos principales de intercambio de bucle es mejorar el rendimiento de la cache para el acceso a elementos de un array Los fallos de cache ocurren si los elementos accedidos de un array contiguo dentro del bucle provienen de una linea de cache diferente Intercambio de bucle puede ayudar a prevenir esto La eficacia del intercambio de bucle depende y debe ser considerado teniendo en cuenta el modelo de cache utilizado por el hardware subyacente y el modelo de arrays utilizado por el compilador 35 Loop invariant code motion Editar Vease tambien Loop invariant code motion Si una cantidad se calcula dentro de un bucle en cada iteracion y su valor es el mismo para cada iteracion se puede mejorar enormemente la eficiencia llevandola fuera del bucle y calcular su valor solo una vez antes de que el bucle comience Esto es particularmente importante con las expresiones de calculo de direcciones generadas por bucles sobre arrays Para la aplicacion correcta esta tecnica debe ser utilizado con la tecnica inversion de bucles porque no todo el codigo es seguro para ser colocado fuera del bucle 36 Optimizacion nido de bucle Editar Vease tambien Optimizacion nido de bucle Algunos algoritmos generalizados como la multiplicacion de arrays tienen un comportamiento muy pobre en cache y accesos excesivos de memoria La optimizacion nido de bucle aumenta el numero de coincidencias de memoria cache mediante la realizacion de la operacion a traves de pequenos bloques y mediante el uso de un bucle intercambio por eso el nombre de nido 37 Reversion de bucle Editar Vease tambien Reversion de bucle Reversion de bucle invierte el orden en el que se asignan valores a la variable del indice Esta es una optimizacion sutil que puede ayudar a eliminar dependencias permitiendo asi otras optimizaciones 38 Desenrollamiento de bucle Editar Vease tambien Desenrollamiento de bucle Desenrollamiento de bucle es una tecnica para optimizar partes de los programas de ordenador La idea es ahorrar tiempo al reducir el numero de instrucciones generales que el equipo tiene que ejecutar en un bucle y asi mejorar la tasa de aciertos de cache y reducir la ramificacion Para lograr esto las instrucciones que se llaman en multiples iteraciones del bucle se combinan en una sola iteracion Esto acelerara el programa si las instrucciones generales del bucle perjudican el rendimiento de forma significativa 39 Division de bucle Editar Vease tambien Division de bucle Division de bucle tambien conocido como loop peeling es una tecnica de optimizacion del compilador Se trata de simplificar un bucle o eliminar dependencias dividiendola en multiples lazos que tienen los mismos cuerpos pero iteran sobre diferentes partes contiguas del rango del indice 40 Loop unswitching Editar Vease tambien Loop unswitching Loop unswitching es una tecnica de optimizacion del compilador Se mueve un condicional dentro de un bucle a fuera del mismo mediante la duplicacion del cuerpo del bucle y colocando una version del mismo en el interior de cada una de las clausulas if y else Esto puede mejorar la paralelizacion del bucle Dado que los procesadores modernos pueden operar rapido en vectores esto aumenta la velocidad 41 Fragmentado de bucle Editar Vease tambien Fragmentado de bucle Fragmentado de bucle tambien conocido como loop tiling es una optimizacion de bucles utilizada por los compiladores para que la ejecucion de ciertos tipos de bucles sea mas eficiente La idea es particionar el espacio de un bucle de iteracion en trozos mas pequenos o bloques a fin de ayudar a asegurar que los datos utilizados en un bucle permanece en la cache hasta que se vuelve a utilizar La division de espacio de un bucle de iteracion conduce a la particion de gran variedad en bloques mas pequenos asi se pueden cargar pequenos trozos de un array en la cache rehusar elementos de la misma y se eliminan los requerimientos de tamano de esta 42 Software pipelining Editar Vease tambien Software pipelining El bucle se reestructura de tal manera que el trabajo realizado en una iteracion se divide en varias partes y hecho en varias iteraciones En un bucle estrecho esta tecnica oculta la latencia entre la carga y el uso de valores Paralelizacion automatica Editar Vease tambien Paralelizacion automatica Un bucle se convierte en codigo multi hilo o vectorizado o incluso los dos con el fin de utilizar varios procesadores simultaneamente en una memoria compartida de multiprocesador SMP de la maquina incluyendo nucleos en multiples maquinas Optimizacion del flujo de datos Editar Las optimizaciones del flujo de datos basadas en el analisis de flujo de datos dependen principalmente de como ciertas propiedades de los datos se propagan por las aristas del grafo de control del flujo Algunos de estos incluyen Eliminacion de la subexpresion comun Editar Vease tambien Eliminacion de la subexpresion comun Eliminar operaciones que son redundantes en sub expresiones logra una reduccion de codigo Asi muchas de las veces a una variable se le asigna un valor y se reutiliza varias veces ya que dicha variable es comun en otras operaciones Pro ejemplo X A SIN Y SIN Y 2 realmente puede ser vista como t SIN Y X A t t 2 43 Plegamiento de constantes Editar Vease tambien Plegamiento de constantes De las tecnicas de optimizacion la mas trivial es la que se refiere a la compactacion del codigo es decir que algunas expresiones pueden ser simplificadas mediante la evaluacion del compilador como puede ser 8 3 C d en la declaracion el compilador la dejara como 11 C d claramente se observa que el compilador reemplaza los numeros 8 y 3 por su resultante por supuesto se aplica casi para cualquier tipo de operacion 43 Propagacion de constantes Editar Vease tambien Propagacion de constantes Similar el plegamiento esta constante se sustituye y es reescrita de una forma implicita Asi por ejemplo cuando hacemos la evaluacion de c 4 valor c 2 este puede ser mejor usado como valor 4 2 y este a su vez ser plegado como se menciono anteriormente 43 Reconocimiento y eliminacion de variables inductivas Editar Ver el apartado de analisis de variable de induccion mas arriba Analisis de punteros y clasificacion de alias Editar Vease tambien Aliasing computacion Vease tambien Analisis de alias Vease tambien Analisis de punteros En presencia de punteros es dificil hacer alguna optimizacion en absoluto ya que potencialmente cualquier variable se ha de cambiar cuando una ubicacion de memoria se la asigna Mediante la especificacion de que punteros pueden tener un alias a una variables punteros no relacionados pueden ser ignorados 44 Eliminacion de almacenamiento muerto Editar Vease tambien Almacenamiento muerto Eliminar las asignaciones a variables que no son posteriormente leidas ya sea por la vida util de la variable o debido a que se sobrescribira el primer valor antes de ser usado Optimizaciones basadas en SSA Editar Vease tambien Static single assignment form Estas optimizaciones se pretende hacer despues de transformar el programa en una static single assignment form en el que se asigna cada variable en un solo lugar Aunque alguna funcion sin SSA son mas eficaces que con SSA Muchas optimizaciones que figuran en otras secciones tambien se benefician sin cambios especiales tales como asignacion de registros Valor de numeracion global Editar Vease tambien Valor de numeracion global El valor de numeracion global elimina la redundancia mediante la construccion de un grafico de valores del programa y a continuacion determinar que valores se calculan con expresiones equivalentes El valor de numeracion global es capaz de identificar alguna redundancia que la eliminacion de subexpresiones comunes no puede y viceversa 45 Propagacion de constantes condicional escasa Editar Vease tambien Propagacion de constantes condicional escasa Efectivamente equivalente a la realizacion iterativa de propagacion de constantes el plegamiento constante y la eliminacion de codigo muerto pero es mucho mas eficiente Esta optimizacion simbolicamente ejecuta el programa al mismo tiempo la propagacion de valores constantes y elimina partes del grafo de control de flujo que son inalcanzables 46 47 Optimizacion del codigo generado Editar Asignacion de registros Editar Vease tambien Asignacion de registros El buen uso de los registros es la caracteristica mas importante de un codigo eficiente Las instrucciones que involucran registros son mas rapidasque las que involucran operandos en memoria Se utiliza la informacion recolectada en analisis de variables vivas Y se usan tecnicas de coloreo de grafos para saber que variable asignar a cada registro 48 Seleccion de instrucciones Editar Vease tambien Seleccion de instrucciones La mayoria de las arquitecturas y en particular las CISC tienen muchos modos de direccionamiento ofrecen varias formas de llevar a cabo una operacion en particular y el uso de secuencias completamente diferentes de instrucciones La naturaleza del conjunto de instrucciones de la maquina objeto determina la dificultad de la seleccion de instrucciones Es importante que el conjunto de instrucciones sea uniforme y completo Si la maquina objeto no apoya cada tipo de datos de una manera uniforme entonces cada excepcion a la regla general exige un tratamiento especial Las velocidades de las instrucciones y las expresiones particulares de la maquina son otros factores importantes Para cada tipo de proposicion de tres direcciones se puede disenar un esqueleto de codigo que perfila el codigo objeto que ha de generarse para esa construccion 49 Planificacion de instrucciones Editar Vease tambien Planificacion de instrucciones La planificacion de instrucciones es una optimizacion importante para los procesadores modernos segmentados lo que evita paradas o burbujas en el pipeline por instrucciones de agrupamiento sin dependencias juntas teniendo cuidado de preservar la semantica original 50 51 Rematerializacion Editar Vease tambien Rematerializacion La rematerializacion vuelve a calcular un valor en lugar de utilizar un acceso a memoria Esto se realiza en conjunto con la asignacion de registros para evitar derrames 52 53 Factorizacion de codigo Editar Si varias secuencias de codigo son identicos o se puede parametrizar o reordenado para ser identicas pueden ser reemplazados con llamadas a una subrutina compartida Esto a menudo puede compartir codigo para subrutinas de configuracion y algunas veces de cola de recursion 54 Trampolines Editar Muchas CPUs tienen pequenas subrutinas de llamadas a instrucciones para acceder a niveles bajos de memoria Un compilador puede ahorrar espacio mediante el uso de estas llamadas pequenas en el cuerpo principal del codigo Cambiar instrucciones en la memoria baja puede acceder a las rutinas en cualquier direccion Esto multiplica el ahorro de espacio de la factorizacion de codigo 54 Reordenamiento de calculos Editar Basado en la Programacion de enteros los compiladores de reestructuracion mejoran la localidad de datos y exponen mas paralelismo con calculos reordenados Optimizando el espacio los compiladores pueden reordenar el codigo para alargar las secuencias que pueden ser un factor en subrutinas 55 Otras optimizaciones Editar Reduccion de potencias Editar Se busca sustituir operaciones costosas por otras mas simples Por ejemplo sustituir productos por sumas evitar la operacion append sustituir productos entre variables inductivas e invariantes de bucle por sumas 56 Eliminacion de los limites de comprobacion Editar Vease tambien Eliminacion de los limites de comprobacion Muchas lenguas por ejemplo Java hace cumplir los limites de comprobacion de todos los accesos a un array Esto es un grave cuello de botella en ciertas aplicaciones tales como codigo cientifico La eliminacion de los limites de comprobacion permite al compilador quitar con seguridad la comprobacion de limites en muchas situaciones donde se puede determinar que el indice debe estar dentro de los limites validos por ejemplo si se trata de una variable de bucle simple 57 Optimizacion del desplazamiento de salto Editar Elige el desplazamiento de la rama mas corta que llega a destino 58 Reordenamiento de bloques de codigo Editar El reordenamiento de bloques de codigo altera el orden de los bloques basicos en un programa con el fin de reducir los saltos condicionales y mejorar la cercania de referencias 59 Eliminacion de codigo muerto Editar Vease tambien Eliminacion de codigo muerto La eliminacion de codigo muerto incluye quitar del codigo fuente cualquier tipo de codigo muerto codigo inalcanzable y codigo redundante 60 Expansion en linea o Expansion de macro Editar Vease tambien Expansion en linea La expansion en linea es una tecnica de optimizacion del compilador que reduce la sobrecarga de una llamada a una funcion simplemente no haciendo la llamada por el contrario el compilador reescribe eficazmente la funcion en el programa como si la definicion de la funcion llamada se inserto en cada sitio que fue invocada 61 Jump threading Editar En este paso consecutivos saltos condicionales predichos total o parcialmente en la misma condicion se fusionan 62 Por ejemplo if c foo if c bar if c foo bar if c foo if c bar if c foo else bar Compresion de macros Editar Una optimizacion de espacio que reconoce secuencias comunes de codigo crea subprogramas macros de codigo que contienen el codigo comun y reemplaza las ocurrencias de las secuencias de codigos comunes con las llamadas a los subprogramas correspondientes 63 Esto se realiza mas eficazmente como una optimizacion del codigo maquina cuando todo el codigo esta presente La tecnica fue utilizada por primera vez para ahorrar espacio en un flujo de bytes interpretativo utilizado en una implementacion de Macro Spitbol en microcomputadoras 64 El problema de determinar un conjunto optimo de macros que minimiza el espacio requerido por un segmento de codigo dado se sabe que es NP completo 63 pero las heuristicas eficientes alcanzan casi resultados optimos 65 Reduccion de la altura de la pila Editar Reorganizar arbol de expresion para minimizar los recursos necesarios para la evaluacion de la expresion 66 Test reordering Editar Si tenemos dos pruebas que son la condicion para algo lo primero que se hace son las pruebas mas simples y solo despues las pruebas complejas Esta tecnica complementa la evaluacion perezosa pero solo se pueden utilizar cuando las pruebas no son dependientes uno del otro Problemas con la optimizacion EditarTemprano en la historia de los compiladores las optimizaciones del compilador no eran tan buenos como las escritos a mano Dado que las tecnologias han mejorado los compiladores buenos a menudo puede generar un mejor codigo que los programadores humanos y una buena optimizacion del codigo objeto puede mejorar el codigo altamente optimizado a mano aun mas Para arquitecturas de CPU RISC y mas aun para el hardware de VLIW un compilador optimizador es clave para obtener un codigo eficiente porque los conjuntos de instrucciones RISC son tan compactos que es dificil para un ser humano programar manualmente o combinar pequenas instrucciones para obtener resultados eficientes En efecto estas arquitecturas fueron disenados para apoyarse en los programadores de compiladores para un rendimiento adecuado Sin embargo los compiladores optimizadores no son de ninguna manera perfectos 67 No hay manera de que un compilador puede garantizar que para todo codigo fuente de un programa se genera la salida equivalente mas rapida o mas pequena tal compilador es imposible ya que resolveria el problema de la parada Esto puede ser demostrado mediante la consideracion de una llamada a una funcion foo Esta funcion devuelve nada y no tiene efectos secundarios no E S no modifica las variables globales ni estructuras de datos etc El programa equivalente mas rapido posible seria simplemente eliminar la llamada a la funcion Sin embargo si la funcion foo en realidad no regresa entonces el programa con la llamada a foo seria diferente del programa sin la llamada el compilador de optimizacion tendra entonces que determinar esto resolviendo el problema de la parada Ademas hay un numero de otras cuestiones practicas con la tecnologia de un compilador optimizador La optimizacion de los compiladores se centran en la mejoras de desempeno relativamente poco profundas y no suelen mejorar la complejidad algoritmica de una solucion Por ejemplo un compilador no va a cambiar una aplicacion de tipo burbuja a un quicksort para mejorar el rendimiento Los compiladores suelen tener que soportar una variedad de objetivos en conflicto como el coste de la velocidad de compilacion ejecucion y calidad del codigo generado Un compilador tipicamente solo se ocupa de una parte de un programa a la vez a menudo el codigo contenido dentro de un unico archivo o modulo el resultado es que no puede tener en cuenta informacion contextual que solo se puede conseguir mediante el procesamiento de los otros archivos La sobrecarga de optimizacion del compilador Cualquier trabajo adicional requiere tiempo todo el programa de optimizacion es mucho tiempo para programas grandes La interaccion a menudo compleja entre las fases de optimizacion hace que sea dificil encontrar una secuencia optima en la que ejecutar las diferentes fases de optimizacion Vease tambien EditarOptimizacion de software Analisis estatico de softwareReferencias Editar Jose M Maria Carrasco La optimizacion una mejora en la optimizacion de programas Consultado el 15 de enero de 2013 Cooper Keith D and Torczon Linda Engineering a Compiler Morgan Kaufmann 2004 ISBN 1 55860 699 8 a b Optimizacion de compiladores Consultado el 15 de enero de 2013 Optimizacion de codigo Consultado el 15 de enero de 2013 a b c Maggie Johnson 4 de agosto de 2008 Code Optimization en ingles Consultado el 15 de enero de 2013 a b Compilador optimizador Consultado el 15 de enero de 2013 Cooper Keith D and Torczon Linda Engineering a Compiler Morgan Kaufmann 2004 ISBN 1 55860 699 8 page 404 Optimizacion de codigo Consultado el 15 de enero de 2013 IBM Optimizaciones locales Consultado el 15 de enero de 2013 Compiladores optimizacion Consultado el 15 de enero de 2013 Cooper Keith D and Torczon Linda Engineering a Compiler Morgan Kaufmann 2004 ISBN 1 55860 699 8 page 407 IBM Global optimizations en ingles Consultado el 15 de enero de 2013 Universidad de Virginia 8 de abril de 2008 Interprocedural Optimizations en ingles Consultado el 15 de enero de 2013 Michael Burke y Linda Torczon Interprocedural Optimization Eliminating Unnecessary Recompilation en ingles Consultado el 15 de enero de 2013 OPTIMIZACIoN DE CoDIGO Consultado el 15 de enero de 2013 Peter Fritzson Christoph Kessler 2011 Vector informatica Code optimization url incorrecta ayuda en ingles Consultado el 15 de enero de 2013 Optimizing your programs en ingles Consultado el 15 de enero de 2013 Optimizacion de compiladores Consultado el 15 de enero de 2013 a b Optimizing compilers en ingles Consultado el 15 de enero de 2013 Clinton F Goss junio de 1986 Machine Code Optimization Improving Executable Object Code New York University Consultado el 24 Mar 2011 La referencia utiliza el parametro obsoleto mes ayuda Chattopadhiay Santanu 2005 Compiler Desing New Delhi Prentice Hall p 225 ISBN 81 203 2726 X isbn incorrecto ayuda Free Software Foundation Options That Control Optimization en ingles Consultado el 16 de enero de 2013 Farhat Masood RISC AND CISC Computer Architecture en ingles Consultado el 16 de enero de 2013 Superescalar processors en ingles Consultado el 16 de enero de 2013 CPU segmentada pipeline Archivado desde el original el 5 de septiembre de 2011 Consultado el 16 de enero de 2013 Introduction of Cache Memory en ingles Consultado el 16 de enero de 2013 Pipeline Consultado el 16 de enero de 2013 Jerarquia de memorias Marzo de 2011 Consultado el 16 de enero de 2013 Utpal Barnerjee Loop paralelization en ingles Consultado el 16 de enero de 2013 Induction variables en ingles Consultado el 16 de enero de 2013 Sebastian Pop Analysis of induction variables using chains of recurrences extensions en frances Consultado el 16 de enero de 2013 Loop Fission en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Fusion en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Inversion en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Interchange en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Invariant Code Motion en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Using Loop Nest Optimization en ingles Archivado desde el original el 3 de febrero de 2013 Consultado el 16 de enero de 2013 Transfomation loop en ingles Consultado el 16 de enero de 2013 Loop Unwinding en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Splitting en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Unswitching en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 Loop Tiling en ingles Archivado desde el original el 23 de mayo de 2012 Consultado el 16 de enero de 2013 a b c FUNDAMENTOS DE OPTIMIZACIoN Archivado desde el original el 2 de enero de 2010 Consultado el 16 de enero de 2013 Mike Acton 1 de junio de 2006 Understanding Strict Aliasing en ingles Consultado el 16 de enero de 2013 Alpern Bowen Wegman Mark N and Zadeck F Kenneth enero de 1988 Detecting Equality of Variables in Programs San Diego CA USA Conference Record of the Fifteenth Annual ACM Symposium on Principles of Programming Languages POPL ACM Press pp 1 11 Wegman Mark N and Zadeck F Kenneth Constant Propagation with Conditional Branches ACM Transactions on Programming Languages and Systems 13 2 April 1991 pages 181 210 Click Clifford and Cooper Keith Combining Analyses Combining Optimizations ACM Transactions on Programming Languages and Systems 17 2 March 1995 pages 181 196 Code optimization en ingles Consultado el 16 de enero de 2013 COMPILADORES Consultado el 16 de enero de 2013 Peter Lee 2009 Introduction to Instruction Scheduling en ingles Consultado el 16 de enero de 2013 Instruction Scheduling en ingles Consultado el 16 de enero de 2013 P Briggs K D Cooper and L Torczon julio de 1992 Proceedings of the SIGPLAN 92 Conference on Programming Language Design and Implementation SIGPLAN Notices 27 7 pp 311 321 Mukta Punjani Register Rematerialization In GCC Archivado desde el original el 28 de abril de 2005 Consultado el 16 de enero de 2013 a b Cx51 Compiler Manual version 09 2001 p155 Keil Software Inc Adams L 1986 Reordering computations for parallel execution Commun appl numer methods 2 263 271 doi 10 1002 cnm 1630020307 Optimizacion de codigo Consultado el 16 de enero de 2013 Xan Gregg 22 de enero de 2006 Range Check Elimination Optimization en ingles Archivado desde el original el 13 de marzo de 2013 Consultado el 16 de enero de 2013 Yu Chen Fuxin Zhang Junio de 2007 Code reordering on limited branch offset NY USA ACM Transactions on Architecture and Code Optimization TACO doi 10 1145 1250727 1250730 Free software fundation 5 de mayo de 2000 Basic Block Reordering en ingles Consultado el 16 de enero de 2013 Code Optimizations Partial Dead Code Elimination en ingles 13 de abril de 2009 Consultado el 16 de enero de 2013 Inline Expansion en ingles Consultado el 16 de enero de 2013 Options That Control Optimization en ingles Consultado el 16 de enero de 2013 a b Clinton F Goss June de 1986 Machine Code Optimization Improving Executable Object Code New York University Consultado el 24 Mar 2011 La referencia utiliza el parametro obsoleto mes ayuda Robert B K Dewar Martin Charles Golumbic Clinton F Goss October de 1979 MICRO SPITBOL Computer Science Department Technical Report No 11 Courant Institute of Mathematical Sciences La referencia utiliza el parametro obsoleto month ayuda Martin Charles Golumbic Robert B K Dewar Clinton F Goss 1980 Macro Substitutions in MICRO SPITBOL a Combinatorial Analysis Proc 11th Southeastern Conference on Combinatorics Graph Theory and Computing Congressus Numerantium Utilitas Math Winnipeg Canada 29 485 495 Advanced Computer Architecture en ingles Consultado el 16 de enero de 2013 Kent Wilken Advanced Compiler Optimization en ingles Archivado desde el original el 30 de enero de 2013 Consultado el 16 de enero de 2013 Bibliografia EditarA Aho R Sethi J D Ullman Compilers Principles Techniques and Tools Reading MA Addison Wesley 1986 J P Bennett Introduction to Compiling Techniques Berkshire England McGraw Hill 1990 R Mak Writing Compilers and Interpreters New York NY Wiley 1991 S Muchnick Advanced Compiler Design and Implementation San Francisco CA Morgan Kaufmann 1997 A Pyster Compiler Design and Construction Reinhold 1988 Enlaces externos EditarAdvanced compiler optimizations for supercomputers Ejemplo se lleva a cabo varias de la optimizaciones nombradas en este articulo a un codigo Datos Q1325106Obtenido de https es wikipedia org w index php title Compilador optimizador amp oldid 129751366, wikipedia, wiki, leyendo, leer, libro, biblioteca,

español

, española, descargar, gratis, descargar gratis, mp3, video, mp4, 3gp, jpg, jpeg, gif, png, imagen, música, canción, película, libro, juego, juegos