2009-10-14 42 views
47

Desde que aprendí acerca -j He usado -j8 alegremente. El otro día estaba compilando una instalación atlas y la marca falló. Finalmente lo rastreé hasta que las cosas se hicieron fuera de orden, y funcionó bien una vez que volví a la marca única. Esto me pone nervioso ¿Qué tipo de condiciones debo tener en cuenta al escribir mis propios archivos make para evitar hacer algo inesperado con make -j?GNU de make

+4

¿Estás seguro de que es hacer de culpa? Escribir makefiles correctos es propenso a errores. –

+1

Meh, incluso los archivos makefiles generados por autotools tienen errores. Intente compilar GCC con -j2 o superior. – LiraNuna

Respuesta

36

pienso hacer -j respetará las dependencias que se especifiquen en el Makefile; es decir, si especifica que objA depende de objB y objC, make no comenzará a trabajar en objA hasta que objB y objC estén completos.

Lo más probable es que su Makefile no especifique el orden de operaciones necesario estrictamente, y es solo suerte que funcione para usted en el caso de un solo hilo.

+10

Eso es correcto. Trabajo en una base de código de aproximadamente 20 millones de líneas, principalmente en C con un poco de C++. Se divide en cientos de componentes, aproximadamente la mitad de los cuales usan marca y la mitad usan mermelada. Siempre hago compilaciones paralelas con la opción -j; de lo contrario, las construcciones tomarían horas. Jam genera sus propias dependencias, por lo que los componentes que lo usan siempre tienen éxito. Pero los componentes que usan makefiles construidos a mano se ahogan ocasionalmente, invariablemente debido a dependencias inadecuadas. –

+0

He visto mucho en la web que '-j' se refiere a la cantidad de CPU con las que desea compilar. He realizado una prueba en mi máquina de 8 núcleos y 'make 'funciona hasta e incluyendo' -j8' (más allá de eso es 100% de uso en todos los núcleos, similar a '-j8'). En cada incremento de entero, vería otro uso del núcleo aumentar aproximadamente en un * 100% *. ¿Diría que es una buena regla general para saber qué especificar como su valor '-j'? – Jacksonkr

+0

La única buena regla que conozco es hacer un "make clean; time make -jn" en su programa, con varios valores de (n), y ver cuánto tiempo tarda cada compilación, y luego ir con la configuración completada en la menor cantidad de tiempo Hay demasiadas variables (velocidad de la CPU, velocidad de la RAM, tamaño de la RAM, velocidad del disco duro, sobrecarga del proceso del sistema operativo, etc.) para poder hacer generalizaciones útiles. –

22

En pocas palabras - asegurarse de que sus dependencias son correctos y completos.

Si está utilizando una marca de un solo subproceso, puede ignorar ciegamente las dependencias implícitas entre los destinos. Al usar la función paralela no puede confiar en las dependencias implícitas. Todos deberían hacerse explícitos. Esta es probablemente la trampa más común. Particularmente si se utilizan objetivos .phony como dependencias.

This enlace es una buena guía sobre algunos de los problemas con la marca paralela.

+0

+1 Para el buen enlace. Ahora siento que puedo confiar en make -j también y cómo solucionar problemas cuando surgen. Bien vale la pena leer. –

7

Si usted tiene un make recursivo, las cosas pueden romper bastante fácilmente. Si no está haciendo una marca recursiva, siempre que sus dependencias sean correctas y completas, no debería tener ningún problema (salvo un error en la marca). Consulte Recursive Make Considered Harmful para obtener una descripción mucho más completa de los problemas con la marca recursiva.

8

He aquí un ejemplo de un problema que me encontré cuando empecé a usar compilaciones paralelas. Tengo un objetivo llamado "nuevo" que utilizo para reconstruir el objetivo desde cero (una compilación "nueva"). En el pasado, codifiqué el objetivo "nuevo" simplemente indicando "limpiar" y luego "compilar" como dependencias.

build: ## builds the default target 
clean: ## removes generated files 
fresh: clean build ## works for -j1 but fails for -j2 

que funcionó bien hasta que empecé a usar compilaciones paralelas, pero con compilaciones paralelas, que intenta hacer ambas cosas "limpia" y "construir" al mismo tiempo. Así que cambié la definición de "nuevo" de la siguiente manera para garantizar el orden correcto de las operaciones.

fresh: 
    $(MAKE) clean 
    $(MAKE) build 

Esto es fundamentalmente solo una cuestión de especificar las dependencias correctamente. El truco es que las construcciones paralelas son más estrictas sobre esto que las compilaciones de subprocesos únicos. Mi ejemplo demuestra que una lista de dependencias para un objetivo dado no necesariamente indica el orden de ejecución.

+2

Recursive make, yuk !. La forma correcta de decir para hacer _porfavor siempre se limpia antes de que build_ sea, por supuesto, 'build: clean'. – bobbogo

+1

@bobbogo: no quiero hacer siempre la limpieza antes de compilar, eso es innecesario en la mayoría de los casos. El objetivo "nuevo" que describí es básicamente un simple script que ejecuta un "make clean" seguido de "make build" (para esos raros momentos en que quiero hacer eso). No veo ningún daño al ejecutar make recursivamente en este escenario. – nobar

+2

¿Qué le parece protegerlo con un condicional: 'ifeq ($ (MAKECMDGOALS), nuevo); construir: limpio; endif' (reemplazar el punto y coma por las líneas nuevas)? – eriktous

1

Es una buena idea tener una prueba automatizada para probar la opción -j de TODOS los archivos make. Incluso los mejores desarrolladores tienen problemas con la opción -j de make. Los problemas más comunes son los más simples.

myrule: subrule1 subrule2 
    echo done 

subrule1: 
    echo hello 

subrule2: 
    echo world 

En la fabricación normal, verá hola -> mundo -> hecho. Con make -j 4, es posible que vea mundo -> hola -> hecho

Donde he visto que esto suceda la mayoría es con la creación de directorios de salida.Por ejemplo:

build: $(DIRS) $(OBJECTS) 
    echo done 

$(DIRS): 
    [email protected] -p [email protected] 

$(OBJECTS): 
    $(CC) ... 
0

Acabo de pensar que agregaría a la respuesta de subsetbrew ya que no muestra el efecto claramente. Sin embargo, agregar algunos comandos de suspensión sí lo hace. Bueno, funciona en Linux.

A continuación, se ejecuta make muestra diferencias con:

  • hacen
  • hacen -j4

all: toprule1 

toprule1: botrule2 subrule1 subrule2 
    @echo toprule 1 start 
    @sleep 0.01 
    @echo toprule 1 done 

subrule1: botrule1 
    @echo subrule 1 start 
    @sleep 0.08 
    @echo subrule 1 done 

subrule2: botrule1 
    @echo subrule 2 start 
    @sleep 0.05 
    @echo subrule 2 done 

botrule1: 
    @echo botrule 1 start 
    @sleep 0.20 
    @echo "botrule 1 done (good prerequiste in sub)" 

botrule2: 
    @echo "botrule 2 start" 
    @sleep 0.30 
    @echo "botrule 2 done (bad prerequiste in top)" 
Cuestiones relacionadas