Pregunta

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

Claramente, cuando el archivador / enlazador crea una biblioteca con archivos de objetos, o los vincula en un ejecutable, incluso las optimizaciones simples son penalizadas. En el ejemplo a continuación, los costos de enrolamiento triviales son del 1.8% en el rendimiento cuando lo realiza el enlazador en lugar del compilador. Parece que la tecnología del compilador debería ser lo suficientemente avanzada para manejar situaciones bastante comunes como esta, pero no está sucediendo.

Aquí hay un ejemplo simple usando 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 la ejecución: 1.8% de impacto al usar el foo vinculado en lugar del foo2 .

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

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

¿Fue útil?

Solución

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, a diferencia del enlazador que tiene que contentarse para operar en la salida de objetos, Mucho menos expresivo que el código que ha visto el compilador. Por lo tanto, el linker y el (los) equipo (s) de desarrollo del compilador (es) dedican menos esfuerzo a realizar optimizaciones de linker que podrían coincidir, en teoría, con los trucos que hace el compilador.

Por cierto, lamento haber distraído tu pregunta original en la discusión sobre ltcg. Ahora entiendo que su pregunta fue un poco diferente, más preocupada por el tiempo de enlace y el tiempo de compilación. Optimizaciones estáticas posibles / disponibles.

Otros consejos

Tu compañero de trabajo está desactualizado. La tecnología está aquí desde 2003 (en el compilador MS C ++): / LTCG . La generación de código de tiempo de enlace está tratando 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 las funciones de alineación a través de los módulos, sino que realmente reajusta el código para optimizar la ubicación de la caché y la bifurcación para una carga específica, consulte Optimizaciones guiadas por perfil . Estas opciones generalmente están reservadas solo para compilaciones de lanzamiento, ya que la compilación puede tardar horas en completarse: vinculará un ejecutable instrumentado, ejecutará una carga de perfilado y luego se vinculará nuevamente con los resultados del perfilado. El enlace contiene detalles sobre qué se optimiza exactamente con LTCG:

  

Inclinación : por ejemplo, si existe   Existe una función A que frecuentemente   llama a la función B, y la función B es   relativamente pequeño, luego guiado por el perfil   Las optimizaciones van en línea a la función B   en la función A.

     

Especulación de llamada virtual : si   llamada virtual, u otra llamada a través de un   puntero de función, frecuentemente se dirige a un   Cierta función, un perfil guiado.   optimización puede insertar un   llamada directa ejecutada condicionalmente a   la función frecuentemente dirigida, y   la llamada directa puede estar en línea.

     

Asignación de registro - Optimizando con   resultados de datos de perfil en mejor   registro de asignación.

     

Optimización básica de bloques - Bloque básico   La optimización permite ejecutar comúnmente   Bloques básicos que se ejecutan temporalmente.   dentro de un marco dado para ser colocado en   El mismo conjunto de páginas (localidad). Esta   minimiza el número de páginas utilizadas,   minimizando así la sobrecarga de memoria.

     

Optimización de tamaño / velocidad - Funciones   donde el programa pasa mucho tiempo   se puede optimizar para la velocidad.

     

Diseño de la función - Basado en la llamada   gráfico y persona que llama / persona llamada   Comportamiento, funciones que tienden a ser.   a lo largo de la misma ruta de ejecución son   colocado en la misma sección.

     

Optimización de rama condicional - con   las sondas de valor, guiadas por perfil   optimizaciones pueden encontrar si un determinado   Se usa el valor en una instrucción de cambio   más a menudo que otros valores. Esta   valor puede ser sacado de la   cambiar la declaración. Lo mismo se puede hacer.   con si / else instrucciones donde el   El optimizador puede ordenar el if / else so   que el bloque if o else es   colocado primero dependiendo de que bloque   con más frecuencia es cierto.

     

Separación de código muerto : código que es   no se llama durante el perfilado se mueve   a una sección especial que se adjunta   Al final del conjunto de secciones.   Esto efectivamente mantiene esta sección   fuera de las páginas de uso frecuente.

     

Separación de código EH : el código EH,   siendo excepcionalmente ejecutado, puede   a menudo se mueven a una sección separada   cuando las optimizaciones guiadas por perfil pueden   Determinar que se producen las excepciones.   solo en condiciones excepcionales.

     

Intrínsecos de memoria : la expansión de   Los intrínsecos se pueden decidir mejor si   Se puede determinar si un intrínseco es   Llamado con frecuencia. Una lata intrínseca   También se optimiza en función del bloque.   Tamaño de los movimientos o copias.

Su compañero de trabajo es más inteligente que la mayoría de nosotros. Incluso si parece un enfoque rudo al principio, el proyecto que se inscribe en un solo archivo .cpp tiene una cosa que los otros enfoques, como la optimización del tiempo de enlace, no tienen y no tendrán por un tiempo - fiabilidad

Sin embargo, lo preguntaste hace dos años, y testifico que muchas cosas han cambiado desde entonces (al menos con g ++). La desvirtualización es mucho más confiable, por ejemplo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top