2010-03-26 26 views
24

No estoy seguro de si esto es posible solo en un Makefile, pero esperaba escribir un Makefile de forma tal que al intentar construir cualquier objetivo en el archivo detecte automáticamente la cantidad de procesadores en el sistema actual y genere el objetivo en paralelo para la cantidad de procesadores.¿Cómo puedo escribir un archivo MAKE para autodetectar y paralelizar la compilación con GNU Make?

Algo así como los siguientes ejemplos de "pseudo-código", ¿pero mucho más limpio?

all: 
    @make -j$(NUM_PROCESSORS) all 

O:

all: .inparallel 
    ... build all here ... 

.inparallel: 
    @make -j$(NUM_PROCESSORS) $(ORIGINAL_TARGET) 

En ambos casos, todo lo que tendría que escribir son:

% make all 

Esperemos que tenga sentido.

ACTUALIZACIÓN: Esperando un ejemplo de Makefile para lo anterior. No está realmente interesado en encontrar el número de procesos, pero está interesado en cómo escribir un archivo MAKE para construir en paralelo sin la opción de línea de comando -j.

+0

No intente hacerlo dentro de un Makefile. La técnica estándar, hasta donde puedo decir, es tener un Makefile trivial de nivel superior que llama a otro, en este caso usando algo como $ (MAKE) -j $ (NUM_PROCESSORS) -f $ (THE_REAL_MAKEFILE). – reinierpost

+0

La solución real sería usar una alternativa para make que esté basada en una técnica de modelado de procesos más poderosa, pero no conozco a ningún buen candidato (lo hago por la técnica, pero no por la herramienta). – reinierpost

Respuesta

5

Después de hurgar un poco en el capítulo 2 de LDD3 y de leer la respuesta de dmckee, se me ocurrió esta no tan buena respuesta de usar dos archivos makef (preferiría solo uno).

$ cat Makefile 
MAKEFLAGS += -rR --no-print-directory 

NPROCS := 1 
OS := $(shell uname) 
export NPROCS 

ifeq ($J,) 

ifeq ($(OS),Linux) 
    NPROCS := $(shell grep -c ^processor /proc/cpuinfo) 
else ifeq ($(OS),Darwin) 
    NPROCS := $(shell system_profiler | awk '/Number of CPUs/ {print $$4}{next;}') 
endif # $(OS) 

else 
    NPROCS := $J 
endif # $J 

all: 
    @echo "running $(NPROCS) jobs..." 
    @$(MAKE) -j$(NPROCS) -f Makefile.goals [email protected] 

%: 
    @echo "building in $(NPROCS) jobs..." 
    @$(MAKE) -j$(NPROCS) -f Makefile.goals [email protected] 
$ cat Makefile.goals 
MAKEFLAGS += -rR --no-print-directory 
NPROCS ?= 1 

all: subgoal 
    @echo "$(MAKELEVEL) nprocs = $(NPROCS)" 

subgoal: 
    @echo "$(MAKELEVEL) subgoal" 

¿Qué opinas sobre esta solución?

Las ventajas que veo es que las personas todavía escriben make para construir. Por lo tanto, no existe un script "controlador" que realice el trabajo NPROCS y make -j$(NPROCS) que las personas deberán saber en lugar de escribir make.

Lo malo es que tendrá que usar explícitamente make -f Makefile.goals para hacer una compilación en serie. Y no estoy seguro de cómo resolver este problema ...

ACTUALIZADO: agregó $ J al segmento de código anterior. Parece que el trabajo funciona bastante bien. A pesar de que se trata de dos archivos makefiles en lugar de uno, sigue siendo bastante fluido y útil.

+0

Me gusta mucho esto ... No me preocuparía el serial construye demasiado, aunque puedes probar algunos ajustes de variables de entorno. Algo como "J = 1 make" es más corto. –

+0

Ah sí, supongo que podría hacer 'make J = 1' y eso sería bastante simple. – dlamotte

+0

If usted cambia 'all' en su' Makefile.goals' para decir 'goal' y combina los dos archivos juntos, luego llama a' @ $ (MAKE) -j $ (NPROCS) $ @ ', ¿no funciona? – Shahbaz

20

La parte de detección va a depender del sistema operativo. He aquí un fragmento que funcionará en Linux y Mac OS X:

NPROCS:=1 
OS:=$(shell uname -s) 

ifeq($(OS),Linux) 
    NPROCS:=$(shell grep -c ^processor /proc/cpuinfo) 
endif 
ifeq($(OS),Darwin) # Assume Mac OS X 
    NPROCS:=$(shell system_profiler | awk '/Number Of CPUs/{print $4}{next;}') 
endif 

para que funcione usted está probablemente va a tener que volver a invocar a make. Entonces su problema está evitando la recursión infinita. Podrías manejar eso teniendo dos makefiles (el primero solo reiniciando el valor -j), pero probablemente sea posible perfeccionarlo.

+1

Puede trabajar alrededor de la recursión usando la variable de entorno ... el sistema de compilación del kernel de Linux es un gran ejemplo de esto –

+0

Tengo más curiosidad en cómo implementar la recursión que en detectar la cantidad de cpus que es bastante simple en comparación con la IMO. – dlamotte

+0

@Sam Estoy algo familiarizado con el sistema de compilación del kernel. ¿Puede darme una pista sobre en qué parte del sistema de compilación podría encontrar este uso específico? – dlamotte

3

voy a saltar sobre la materia $(NPROCS) detección, pero aquí es cómo se puede hacer esto en una sola Makefile (esto es probablemente GNU Make específico, sino que se parece a la variante que se está ejecutando):

ifeq ($(NPROCS),) 
# Code to set NPROCS... 
%: 
    $(MAKE) -j$(NPROCS) NPROCS=$(NPROCS) 
else 
# All of your targets... 
endif 

Ver Defining Last-Resort Default Rules y Overriding Part of Another Makefile en el GNU Make Manual.

0

Si leo la pregunta correctamente, el objetivo es paralelizar el proceso de construcción tanto como sea posible.La página del manual make indica lo siguiente

Si la opción -j se proporciona sin un argumento, make no limitará el número de trabajos que se pueden ejecutar simultáneamente.

¿No es básicamente la solución que desea? Si tu Makefile tiene suficientes objetivos paralelos, usarás todas tus CPU y si los objetivos no son paralelos, la opción -j no ayudará.

+5

Este es un equivalente de fork bomb si tienes cientos de archivos fuente. –

+0

Parece que es lo que hace Eclipse cuando usa la opción de compilación paralela "Usar número de trabajos óptimos" – regomodo

1

Si desea que sea automático, puede anular su comando de marca típico para ser un alias para sí mismo en su .bashrc en su directorio de inicio.

Ejemplo:

alias make="make -j" 

O usted podría hacer algo como:

alias jmake="make -j" 

en el caso de que usted no quiere anularlo, pero desea una rápida y fácil (y memorable) forma de ejecutar make en paralelo.

+1

La idea es hacer que funcione para cualquiera que use la fuente, no requiera cambios en el entorno de cada desarrollador. – dlamotte

5

Esto es lo que fui con:

ifeq ($(OS),Linux) 
     NUMPROC := $(shell grep -c ^processor /proc/cpuinfo) 
else ifeq ($(OS),Darwin) 
     NUMPROC := $(shell sysctl hw.ncpu | awk '{print $$2}') 
endif 

# Only take half as many processors as available 
NUMPROC := $(shell echo "$(NUMPROC)/2"|bc) 

ifeq ($(NUMPROC),0) 
     NUMPROC = 1 
endif 
+4

En lugar de pasar la salida 'sysctl' por' awk', simplemente solicite el valor solamente: 'sysctl -n hw.ncpu' –

+1

La forma más sencilla de determinar el sistema operativo (funciona en Linux y Mac antiguos) es ' NUMPROC = \ 'getconf _NPROCESSORS_ONLN \' ' –

6

Acabo de añadir esto a la parte superior de mi Makefile. Permite crear cualquier cantidad de trabajos, pero intenta mantener el promedio de carga por debajo de la cantidad de núcleos de CPU.

MAKEFLAGS+="-j -l $(shell grep -c ^processor /proc/cpuinfo) " 

Tenga en cuenta que esto es específico de Linux.

+0

Desafortunadamente, este tipo de cosas no funcionan bien para todas las situaciones, así que experimenta con esto cuidadosamente antes de usar. He experimentado con él y lo he fluctuado enormemente entre demasiados trabajos paralelos y compilado en serie hasta que el promedio de carga cae por debajo del umbral. –

+0

Aún más corto: 'MAKEFLAGS + =" - j $ (shell nproc) "' - tenga en cuenta que puede dispararse fácilmente en un pie con este – sanmai