2009-09-09 16 views
14

Recientemente, un compañero de trabajo me señaló que la compilación de todo en un solo archivo creaba un código mucho más eficiente que la compilación de archivos de objetos separados: incluso con la optimización del tiempo de enlace activada. Además, el tiempo de compilación total del proyecto disminuyó significativamente. Dado que una de las razones principales para usar C++ es la eficiencia del código, esto me sorprendió.¿Por qué la optimización del enlazador es tan pobre?

Claramente, cuando el archivador/enlazador crea una biblioteca de archivos de objetos, o los vincula en un archivo ejecutable, incluso las optimizaciones simples son penalizadas. En el siguiente ejemplo, la creación trival cuesta un 1.8% de rendimiento cuando lo hace el vinculador en lugar del compilador. Parece que la tecnología de compilación debe ser lo suficientemente avanzada como para manejar situaciones bastante comunes como esta, pero no está sucediendo.

edición:

Aquí está un ejemplo sencillo utilizando Visual Studio 2008:

#include <cstdlib> 
#include <iostream> 
#include <boost/timer.hpp> 

using namespace std; 

int foo(int x); 
int foo2(int x) { return x++; } 

int main(int argc, char** argv) 
{ 
    boost::timer t; 

    t.restart(); 
    for (int i=0; i<atoi(argv[1]); i++) 
    foo (i); 
    cout << "time : " << t.elapsed() << endl; 

    t.restart(); 
    for (int i=0; i<atoi(argv[1]); i++) 
    foo2 (i); 
    cout << "time : " << t.elapsed() << endl; 
} 

foo.cpp

int foo (int x) { return x++; } 

resultados de ejecución: 1,8% impacto en el rendimiento al uso de foo ligado vez de foo2 en línea.

$ ./release/testlink.exe 100000000 
time : 13.375 
time : 13.14 

Y sí, los indicadores de optimización del enlazador (/ LTCG) están activados.

+4

¿Qué compilador estás usando? VC++ tiene una opción llamada Whole Program Optimization, que creo que hará lo que esté pidiendo. –

+0

La mayoría de las compilaciones modernas tienen la opción de "optimización del tiempo de enlace", pero generalmente es opcional, ya que ralentiza significativamente el enlace. ¿Lo has habilitado en tu prueba? Normalmente permite la creación de objetos cruzados, entre otras cosas. –

+0

Una de las principales razones de C++ es la eficiencia del código * fuente *; aplicar este argumento al código objeto es engañoso. – harpo

Respuesta

3

No soy un especialista en compiladores, pero creo que el compilador tiene mucha más información disponible para optimizar, ya que opera en un árbol de idiomas, como opuesto al enlazador que tiene que contentarse con operar en el salida de objeto, mucho menos expresiva que el código que el compilador ha visto. Por lo tanto, los equipos de desarrollo de compiladores y compiladores invierten menos esfuerzo en realizar optimizaciones de enlazadores que puedan coincidir, en teoría, con los trucos que hace el compilador.

BTW, lamento haber distraído su pregunta original en la discusión de ltcg, ahora entiendo que su pregunta fue un poco diferente, más preocupada por el tiempo de enlace vs.tiempo de compilación optimizaciones estáticas posibles/disponibles.

+0

Esto no es cierto para Visual C++. Al compilar con/GL y enlazar con/LTCG, el compilador produce una representación intermedia en los archivos de objeto en lugar del código de máquina real, y el vinculador toma esa representación intermedia y realiza la compilación real. –

27

Su compañero de trabajo está desactualizado. La tecnología ha estado aquí desde 2003 (en el compilador MS C++): /LTCG. La generación de código de tiempo de enlace trata exactamente este problema. Por lo que sé, el GCC tiene esta característica en el radar para el compilador de la próxima generación.

LTCG no solo optimiza el código como funciones internas en los módulos, sino que realmente reagrupa el código para optimizar la ubicación de la memoria caché y la ramificación para una carga específica, consulte Profile-Guided Optimizations. Estas opciones están normalmente reservadas solo para compilaciones de Release, ya que la compilación puede tardar horas en finalizar: enlazará un ejecutable instrumentado, ejecutará una carga de creación de perfiles y luego volverá a vincularse con los resultados del perfilado. El enlace contiene detalles acerca de lo que se ha optimizado con LTCG:

Inlining - Por ejemplo, si hay existe una función A que con frecuencia llamadas de función B y B es la función relativamente pequeño, entonces perfil- guiadas optimizaciones función en línea B en función de A.

especulación llamada virtual - Si una llamada virtual , u otra llamada a través de un puntero función, con frecuencia se dirige a una función determinada de , una optimización guiada por perfil puede insertar una llamada directa ejecutada condicionalmente a la función de destino frecuente y la llamada directa puede estar en línea.

Registro de Asignación - Optimización con resultados de los datos en el perfil mejor asignación de registros.

optimización del bloque básico - bloque básico optimización permite que comúnmente se ejecuta bloques básicos que temporalmente se ejecutan dentro de un marco determinado para ser colocados en el mismo conjunto de páginas (localidad). Esto minimiza el número de páginas utilizadas, , lo que minimiza la sobrecarga de memoria.

Tamaño/velocidad Optimización - Funciones donde el programa pasa mucho tiempo puede ser optimizado para la velocidad.

Layout Función - Basado en la llamada gráfico y perfilado comportamiento de llamadas/abonado llamado , las funciones que tienden a ser a lo largo de la misma ruta de ejecución son colocado en la misma sección.

condicional Optimización Branch - Con las sondas de valor, guiada por perfiles optimizaciones pueden encontrar si un valor dado en una sentencia switch se utiliza con más frecuencia que otros valores. Este valor de se puede extraer de la instrucción de cambio . Lo mismo se puede hacer con instrucciones if/else donde el optimizador puede ordenar el if/else para que que el bloque if o else sea colocado primero dependiendo de qué bloque sea más frecuente cierto.

Muerto Separación Código - Código que es no se llama durante el perfilado se mueve a una sección especial que se adjunta hasta el final de la serie de secciones. Esto mantiene efectivamente esta sección fuera de las páginas de uso frecuente.

EH Separación Código - El código de EH, siendo excepcionalmente ejecutados, puede menudo se trasladó a una sección separada cuando optimizaciones guiada por perfiles pueden determinar que las excepciones se producen sólo en condiciones excepcionales.

memoria intrínseco - La expansión de intrínsecos se puede decidir mejor si se puede determinar si un intrínseco es llamado con frecuencia. También se puede optimizar una lata intrínseca según el tamaño del bloque de movimientos o copias.

+4

En cuanto al soporte de gcc, consulte http://gcc.gnu.org/wiki/LinkTimeOptimization –

+0

Gracias por el enlace Pavel –

+0

La generación de código de tiempo de enlace es más lenta que compilar como un archivo. –

1

su compañero de trabajo es más inteligente que la mayoría de nosotros. Incluso si parece un enfoque muy refinados, inlining proyecto en un único archivo .cpp tiene una cosa que los otros enfoques como el enlace en tiempo de optimización de no tener y no tener para rato - fiabilidad

Sin embargo, preguntaste esto hace 2 años, y atestiguo que muchas cosas han cambiado desde entonces (con g ++ al menos), la desvirtualización es mucho más confiable, por ejemplo

Cuestiones relacionadas