2009-08-23 17 views
20

Tengo una secuencia de comandos de SCons que demora alrededor de 10 segundos para descubrir que no es necesario reconstruir nada, lo cual se siente terriblemente largo para lo que es esencialmente un proyecto bastante pequeño. La lectura de la propia SConscript sólo toma un par de segundos, la mayoría de las veces es gastar en el:¿Cómo optimizar el tiempo de inicio de un script SCons?

scons: Building targets ... 

paso.

¿Cómo puedo saber qué está haciendo exactamente scons en este momento? ¿Y qué otro consejo general se puede dar para escribir guiones rápidos de SCons?

Respuesta

8

hice un poco de ensayo y error para averiguar por qué SCons es lento, algunos hallazgos hasta el momento (resultados exactos serían, por supuesto, varían en función de la estructura y la complejidad de la secuencia de comandos SCons):

  • CacheDir() tiene sin impacto negativo notable.
  • Decider() tiene un impacto muy leve, no vale la pena molestarse con.
  • El uso de variant_dir/VariantDir() aumenta el tiempo de construcción en aproximadamente un 10%.
  • Al leer el archivo SConstruct se tarda alrededor del 10% de la llamada a scons completa.
  • El mayor impacto parecen ser las dependencias de la biblioteca, tener Gtkmm en el proyecto duplicó el tiempo de compilación para mí.

soluciones posibles:

  • no hago la reconstrucción completa, pero sólo reconstruir el directorio/módulo que está trabajando (en lugar de scons -uscons -D).
  • Haga que las piezas del SConscript sean opcionales, por lo que solo se reconstruyen cuando se las llama manualmente.
  • usar la bandera compilador -isystem para la biblioteca incluye en lugar de -I, este cambio solo hizo caer el tiempo de construcción de 10.5sec a 6sec para mí, que se puede lograr fácilmente con un poco de llamada SED:

    env.ParseConfig('pkg-config --cflags --libs gtkmm-2.4 | sed "s/-I/-isystem/g"')

    No estoy seguro de por qué funciona esto, supongo que reduce las dependencias que gcc genera y, por lo tanto, a su vez las dependencias que scons rastrea.

Usando CacheDir()scons -j N y es, por supuesto, también recomiendo, pero sólo acelera la construcción de realidad, no la evaluación de la propia escritura de SCons.

+12

SCons no utiliza el corrector de dependencias de GCC, sino que tiene su propio conjunto de expresiones regulares basado en Python para buscar incluye. El uso de -isystem es más rápido simplemente porque está ocultando todos esos encabezados de SCons. Si alguno de esos encabezados cambia, SCons no puede decirlo. Lo que estás haciendo es esencialmente sacrificar una gran parte del DAG representado por dependencias implícitas (encabezados). Esto acelera el paso transversal a costa de la corrección. – BenG

9

scons md5-suma los archivos para descubrir que han cambiado, por lo que prácticamente md5sum todos sus archivos.

Puede indicarle que solo use las marcas de tiempo para decidir qué reconstruir y no tener que MD5sum todos los archivos cada vez, al igual que 'make' hace, lo que debería acelerar las cosas. Puede ser más frágil. p.ej. si un archivo ha cambiado dentro de 1 segundo de la última vez que se creó, scons no lo notará. Utilice

env.Decider('timestamp-newer') 

También hay MD5-marca de tiempo, que comprobará la marca de tiempo en primer lugar, a continuación, comparar el contenido mediante Md5 si ha cambiado realmente, si la marca de tiempo es más reciente.

env.Decider('MD5-timestamp') 

Otra opción fácil para acelerar las cosas es ejecutar construcciones paralelas usando el parámetro -j.

scons -j 2 

En mi caja de 2 núcleos, -j 3 generalmente da la mayor aceleración.

Algunos resultados de lo que scons está haciendo se pueden hacer con el argumento --debug para llamar a scons, consulte la página de manual para las diversas opciones.

+2

Jugué con Decider() pero tiene un impacto cercano a cero en el tiempo de compilación (es decir, en el rango de 0.1 seg). También ya soy "scons -j 2", pero eso solo ayuda con la construcción en sí, no con el procesamiento de dependencias, esa parte parece usar solo un núcleo único. – Grumbel

+0

¿Debo configurar el Decider en cada archivo SConscript, o solo en el archivo SConstruct inicial, para usarlo en toda la compilación? –

+0

El archivo SConstruct superior es suficiente. (aunque si de alguna manera crea entornos separados en otro lugar que no se copian/clonan desde el entorno en el que llama a Decider(), debe llamar de nuevo para ese entorno. - Si no está instantando ningún entorno, simplemente llame Decider() en lugar de env.Decider()) – nos

25

(robados directamente de http://www.scons.org/wiki/GoFastButton)

El comando 'scons --max-deriva = 1 --implicit-deps-sin cambios' se ejecutará su construcción lo más rápido posible.

OR:

  • env.Decider ('MD5-timestamp'): a partir de SCons 0,98, se puede ajustar la función de Decider en un entorno. MD5-timestamp dice que si la marca de tiempo coincide, no se moleste en volver a MD5 el archivo. Esto puede dar grandes aceleraciones. Consulte la página de manual para obtener información.
  • --max-drift: de forma predeterminada, SCons calculará la suma de comprobación MD5 de cada archivo fuente en su compilación cada vez que se ejecuta, y solo almacenará en caché la suma de comprobación después de que el archivo tenga 2 días. Este valor predeterminado de 2 días es para proteger del sesgo del reloj de NFS o de los sistemas de control de revisiones. Puede ajustar este retraso usando --max-drift = SECONDS donde SECONDS es una cantidad de segundos. La disminución de SECONDS puede mejorar la velocidad de desarrollo al eliminar los cálculos de suma de comprobación MD5 superfluos. En lugar de especificar esto en la línea de comando de cada ejecución, puede establecer esta opción dentro de su archivo SConstruct o SConscript usando "SetOption ('max_drift', SECONDS)".
  • --implicit-deps-unchanged: de manera predeterminada, SCons volverá a analizar todos los archivos fuente para las dependencias implícitas (por ejemplo, el encabezado C/C++ # incluye), lo que puede ser un proceso costoso. Puede indicar a SCons que almacene en caché las dependencias implícitas entre invocaciones utilizando --pplicit-deps-unchanged. Al utilizar estas opciones, prometes a SCons que no cambiaste ninguna de las dependencias implícitas desde la última vez que se ejecutó. Si cambia las dependencias implícitas, el uso de --implicit-deps-changed hará que se vuelvan a ubicar y almacenar en caché. (No puede establecer esta opción desde los archivos SConstruct o SConscript.)
  • - caché implícita: Esta opción le dice a SCons que guarde inteligentemente caché de las dependencias implícitas. Intenta determinar si las dependencias implícitas han cambiado desde la última compilación y, de ser así, las volverá a calcular. Suele ser más lento que usar --implicit-deps-sin cambios, pero también es más preciso. En lugar de especificar esto en la línea de comando de cada ejecución, puede establecer esta opción dentro de su archivo SConstruct o SConscript usando "SetOption ('implicit_cache', 1)".
  • CPPPATH: Normalmente le dice a Scons acerca de incluir directorios estableciendo la variable de construcción CPPPATH, que hace que SCons busque esos directorios cuando realiza análisis de dependencias implícitos y también incluye esos directorios en la línea de comandos de compilación. Si tiene archivos de encabezado que nunca o rara vez cambian (por ejemplo, encabezados de sistema o encabezados de tiempo de ejecución de C), puede excluirlos de CPPPATH e incluirlos en la variable de construcción CCFLAGS, lo que hace que SCons ignore esos directorios cuando escanea para dependencias implícitas El ajuste cuidadoso de los directorios de inclusión de esta manera generalmente puede dar como resultado un aumento de velocidad dramático con muy poca pérdida de precisión.
  • Evite escaneos RCS y SCCS utilizando env.SourceCode (".", Ninguno) - esto es especialmente interesante si usa muchos encabezados c o C++ en su programa y su sistema de archivos es remoto (nfs, samba) .
  • Al usar "BuildDir", utilícelo con "duplicate" establecido en 0: "BuildDir (dir1, dir2, duplicate = 0". Esto provocará que scons invoque Builders utilizando los nombres de ruta de los archivos fuente en src_dir y la ruta nombres de archivos derivados dentro de dir_desarrollo. Sin embargo, esto puede causar problemas de compilación si los archivos fuente se generan durante la compilación, si las herramientas invocadas están codificadas para poner los archivos derivados en el mismo directorio que los archivos fuente.
  • En un multi -procesador de máquina puede ser beneficioso ejecutar múltiples trabajos a la vez - use --jobs N (donde N es la cantidad de procesadores en su máquina), o "SetOption ('num_jobs', N)" dentro de su SConstruct o SConscript. En máquinas Windows, la cantidad de procesadores está disponible en la variable de entorno 'NUMBER_OF_PROCESSORS'.
  • Si tiene más de unas pocas docenas de preprocesador ("-DFOO1 -DFOO2"), puede encontrar al usar --perfile que SCons está pasando mucho tiempo en la función subst(), por lo general solo agrega la cadena -D al define una y otra vez. Esto realmente puede ralentizar las construcciones donde nada ha cambiado. Con más de 100 define, vi una reducción del tiempo de compilación de "no hacer nada" de 35 a 20 usando la idea descrita en "Caché del CPPDEFINES" en otro lugar de esta página.

Otro truco para hacer las cosas más rápido es evitar volver a vincular programas cuando una biblioteca compartida ha sido modificada pero no reconstruida. Ver SharedLibrarySignatureOverride

5

Cuando SCons crea primero Environment realiza una serie de búsquedas para ver qué herramientas están disponibles. Puede evitar verificaciones innecesarias y acelerar las SCons al elegir explícitamente las herramientas en DefaultEnvironment antes de crear su primer env.

DefaultEnvironment(tools=[]) 
7

Mudanza de terceros incluye salir de CPPPATH y entrar a CCFLAGS hizo una gran diferencia. Para nuestro proyecto con 12 directorios de inclusión externos (incluidos boost y python), una compilación no hacer pasó de 30 a 3, una aceleración 10 veces mayor.

+1

La misma experiencia para mí. Tener rutas de Boost en CPPPATH solo hace explotar el árbol de dependencia por muchos factores.Hay algo sobre las libs de impulso que lo causa. Posiblemente porque los archivos .hpp tienen muchas inclusiones. –

2

Añadir a su SConscript algo como

if 'explain' in GetOption("debug"): 
    Progress('Evaluating $TARGET\n') 

y correr con --debug=explain. Verá qué SCons pasa tiempo evaluando

Cuestiones relacionadas