2012-03-16 9 views
8

Tenía un solo archivo de origen que tenía todas las definiciones de clase y funciones.Archivo de fuente múltiple ejecutable más lento que el ejecutable de archivo de origen único

Para una mejor organización, moví las declaraciones de clase (.h) y las implementaciones (.cpp) en archivos separados.

Pero cuando los compilé, resultó en un ejecutable más lento que el que obtuve del ejecutable de fuente única. Es alrededor de 20-30 segundos más lento para la misma entrada. No cambio ningún código.

¿Por qué sucede esto? ¿Y cómo puedo hacerlo más rápido de nuevo?

Actualización: El ejecutable de una sola fuente se completa en 40 segundos, mientras que el ejecutable de varias fuentes tarda 60. Y me refiero al tiempo de ejecución y no a la compilación.

+3

20-30 segundos de la duración total? –

+2

Además, ¿está vinculando/compilando en la línea de comandos, o usando un IDE como VS? Obviamente va a llevar más tiempo vincular los archivos fuente juntos que si todo estuviera en un solo archivo, pero la diferencia de 20-30 segundos es demasiado larga para todas las aplicaciones, excepto para las grandes. Si tuviera que adivinar, y esto es solo una suposición, diría que tendrías que expandir tu fuente alrededor de miles de archivos para obtener ese tipo de tiempos de compilación. – prelic

+1

¿Está seguro de que no está compilando utilizando indicadores de "depuración"? 20 segundos es ** mucho **. – mfontanini

Respuesta

10

Creo que su programa se ejecuta más rápido cuando se compila como un único archivo porque en este caso el compilador tiene más información, necesaria para optimizar el código. Por ejemplo, puede alinear automáticamente algunas funciones, lo que no es posible en caso de compilación separada.

Para hacerlo más rápido de nuevo, puede intentar habilitar el optimizador de tiempo de enlace (o el optimizador de programa completo) con esta opción: -flto.


Si -flto opción no está disponible (y que sólo está disponible a partir de gcc 4.6) o si no desea utilizarlo, por alguna razón, de que tiene al menos 2 opciones:

  1. Si divide su proyecto solo en for better organization, puede crear un único archivo de origen (como all.cxx) y #include todos los archivos de origen (todos los demás archivos *.cxx) en este archivo. Entonces solo necesita compilar este all.cxx, y todas las optimizaciones del compilador están disponibles nuevamente. O bien, si lo divide también para hacer una compilación incremental, puede preparar 2 opciones de compilación: compilación incremental y compilación unitaria. El primero construye todas las fuentes independientes, el segundo - solo all.cxx. Ver más información en este here.
  2. Puede encontrar funciones que le cuesten el rendimiento después de dividir el proyecto y moverlas a la unidad de compilación, donde se usan o al archivo de encabezado. Para hacer esto, comience con perfilar (vea "What can I use to profile C++ code in Linux?"). Investigue más a fondo partes del programa que tengan un impacto significativo en el rendimiento del programa; Aquí hay 2 opciones: o use Profiler nuevamente para comparar los resultados de compilaciones incrementales y unitarias (pero esta vez necesita un perfilador de muestreo, como oprofile, mientras que un perfilador de instrumentos, como gprof, es demasiado pesado para esta tarea); o aplique una estrategia 'experimental', como se describe en gbulmer.
+0

Piense también. Probablemente en línea. –

+0

¿Eso es aplicable con g ++? – questions

+3

@ preguntas, sí, gcc puede hacer la optimización del programa completo y para activarlo, use la opción '-flto'. gcc y g ++ son solo 2 nombres diferentes para el mismo compilador, utilizados para c o C++ en consecuencia. –

5

Esto probablemente tiene que ver con link time optimization. Cuando todo su código está en un único archivo fuente, el compilador tiene más conocimiento sobre lo que hace su código para que pueda realizar más optimizaciones. Una de esas optimizaciones está en línea: ¡el compilador solo puede alinear una función si conoce su implementación en tiempo de compilación!

Estas optimizaciones también se pueden realizar en tiempo de enlace (en lugar de tiempo de compilación) pasando el indicador -flto a gcc, tanto para la compilación como para la etapa de enlace (consulte here).

+0

¿Querías decir -flto? ¿Eso es aplicable con g ++? – questions

+1

Sí, error tipográfico, lo siento. '-flto' también funciona con g ++ - diablos, de acuerdo con la página de manual incluso funciona cuando se mezclan idiomas. – Thomas

1

Este es un enfoque más lento para volver al tiempo de ejecución más rápido, pero si se quería obtener una mejor comprensión de lo que está causando el cambio grande, se podría hacer un par de 'experimentos'

Un experimento sería para encontrar qué función podría ser responsable del gran cambio. Para hacer eso, podría 'perfilar' el tiempo de ejecución de cada función.

Por ejemplo, utilice gprof GNU, parte de GNU binutils: http://www.gnu.org/software/binutils/
Docs en: http://sourceware.org/binutils/docs-2.22/gprof/index.html

Esto medirá el tiempo consumido por cada función en su programa, y ​​donde fue llamado desde. Hacer estas mediciones probablemente tendrá un "efecto Heisenberg"; tomar medidas afectará el rendimiento del programa. Por lo que es posible que desee probar un experimento para encontrar qué clase está haciendo la mayor diferencia.

Trate de hacerse una idea de cómo varía el tiempo de ejecución entre tener el código fuente de clase en la fuente principal y el mismo programa pero con la clase compilada y vinculada por separado.

Para obtener una implementación de clase en el programa final, puede compilarla y vincularla, o simplemente #incluirla al programa 'principal' y luego compilar main.

Para que sea más fácil de tratar permutaciones, usted podría cambiar un # include o desactivar mediante #if:

#if defined(CLASSA) // same as #ifdef CLASSA 
#include "classa.cpp" 
#endif 
#if defined(CLASSB) 
#include "classb.cpp" 
#endif 

A continuación, puede controlar qué archivos se #include usando indicadores de línea de comando para el compilador, por ejemplo,

g++ -DCLASSA -DCLASSB ... main.c classc.cpp classd.cpp classf.cpp 

Sólo se puede tardar unos minutos para generar las permutaciones de los -Dflags y comandos de enlace. Entonces tendrías una forma de generar todas las permutaciones de compilación 'en una unidad' frente a las vinculadas por separado, luego ejecutar (y cronometrar) cada una.

que asumen sus archivos de cabecera están envueltos en el habitual

#ifndef _CLASSA_H_ 
#define _CLASSA_H_ 
//... 
#endif 

A continuación, tendría alguna información acerca de la clase que es importante.

Estos tipos de experimentos pueden arrojar algo de información sobre el comportamiento del programa y el compilador que podría estimular otras ideas para mejoras.

Cuestiones relacionadas