Marca GNU: ¿debería el número de trabajos ser igual al número de núcleos de CPU en un sistema?

87

Parece haber cierta controversia sobre si se supone que la cantidad de trabajos en GNU make es igual a la cantidad de núcleos, o si puede optimizar el tiempo de compilación agregando un trabajo adicional que se puede poner en cola mientras los otros "funcionan" .

¿Es mejor usar -j4o -j5en un sistema de cuatro núcleos?

¿Ha visto (o hecho) alguna evaluación comparativa que apoye a uno u otro?

Johan
fuente
8
Solo como sugerencia, puede usar make `nproc`para hacer un script independiente de la CPU :)
VivienG
Si tiene una combinación de recetas que están unidas a io y a cpu, entonces potencialmente querrá muchas más que NCPU. Considere también agregar opciones -lX. Esta no es realmente una pregunta que se pueda responder, aparte de "depende de su hardware y tareas".
James Moore
Es técnicamente posible ver una mejora. Necesita un disco lento, no suficiente RAM y muchos archivos de código fuente pequeños. Más fácil de conseguir hace una década.
Hans Passant

Respuestas:

56

Yo diría que lo mejor que puede hacer es compararlo usted mismo con su entorno y carga de trabajo particulares. Parece que hay demasiadas variables (tamaño / número de archivos de origen, memoria disponible, almacenamiento en caché del disco, si el directorio de origen y los encabezados del sistema están ubicados en diferentes discos, etc.) para una respuesta única para todos.

Mi experiencia personal (en una MacBook Pro de 2 núcleos) es que -j2 es significativamente más rápido que -j1, pero más allá de eso (-j3, -j4, etc.) no hay una aceleración medible. Entonces, para mi entorno, "trabajos == número de núcleos" parece ser una buena respuesta. (YMMV)

David Gelhar
fuente
57

Ejecuté mi proyecto doméstico en mi computadora portátil de 4 núcleos con hyperthreading y registré los resultados. Este es un proyecto bastante pesado para el compilador, pero incluye una prueba unitaria de 17,7 segundos al final. Las compilaciones no son muy intensivas en IO; hay mucha memoria disponible y, si no, el resto está en un SSD rápido.

1 job        real   2m27.929s    user   2m11.352s    sys    0m11.964s    
2 jobs       real   1m22.901s    user   2m13.800s    sys    0m9.532s
3 jobs       real   1m6.434s     user   2m29.024s    sys    0m10.532s
4 jobs       real   0m59.847s    user   2m50.336s    sys    0m12.656s
5 jobs       real   0m58.657s    user   3m24.384s    sys    0m14.112s
6 jobs       real   0m57.100s    user   3m51.776s    sys    0m16.128s
7 jobs       real   0m56.304s    user   4m15.500s    sys    0m16.992s
8 jobs       real   0m53.513s    user   4m38.456s    sys    0m17.724s
9 jobs       real   0m53.371s    user   4m37.344s    sys    0m17.676s
10 jobs      real   0m53.350s    user   4m37.384s    sys    0m17.752s
11 jobs      real   0m53.834s    user   4m43.644s    sys    0m18.568s
12 jobs      real   0m52.187s    user   4m32.400s    sys    0m17.476s
13 jobs      real   0m53.834s    user   4m40.900s    sys    0m17.660s
14 jobs      real   0m53.901s    user   4m37.076s    sys    0m17.408s
15 jobs      real   0m55.975s    user   4m43.588s    sys    0m18.504s
16 jobs      real   0m53.764s    user   4m40.856s    sys    0m18.244s
inf jobs     real   0m51.812s    user   4m21.200s    sys    0m16.812s

Resultados básicos:

  • Escalar al recuento de núcleos aumenta el rendimiento de forma casi lineal. El tiempo real bajó de 2,5 minutos a 1,0 minuto (2,5 veces más rápido), pero el tiempo necesario durante la compilación aumentó de 2,11 a 2,50 minutos. El sistema apenas notó carga adicional en este bit.
  • Escalar desde el recuento de núcleos al recuento de subprocesos aumentó enormemente la carga de usuarios, de 2,50 minutos a 4,38 minutos. Lo más probable es que esta casi duplicación se deba a que las otras instancias del compilador querían usar los mismos recursos de CPU al mismo tiempo. El sistema se está cargando un poco más con solicitudes y cambio de tareas, lo que hace que se utilice en 17,7 segundos. La ventaja es de unos 6,5 segundos en un tiempo de compilación de 53,5 segundos, lo que supone una aceleración del 12%.
  • El escalado del número de hilos al número de hilos dobles no dio una aceleración significativa. Los tiempos en 12 y 15 son probablemente anomalías estadísticas que puede ignorar. El tiempo total necesario aumenta ligeramente, al igual que el tiempo del sistema. Es muy probable que ambos se deban a un mayor cambio de tareas. No hay ningún beneficio para esto.

Supongo que ahora: si haces otra cosa en tu computadora, usa el recuento de núcleos. Si no es así, utilice el recuento de hilos. Superarlo no muestra ningún beneficio. En algún momento, la memoria se limitará y colapsará debido a eso, lo que hará que la compilación sea mucho más lenta. La línea "inf" se agregó en una fecha mucho más tarde, lo que me dio la sospecha de que hubo un estrangulamiento térmico para los trabajos 8+. Esto muestra que para este tamaño de proyecto no hay ningún límite de memoria o rendimiento en vigor. Sin embargo, es un proyecto pequeño, con 8 GB de memoria para compilar.

dascandy
fuente
Según stackoverflow.com/questions/56272639/… , puede obtener una ventaja al ejecutar más tareas de las que tiene CPU, pero solo si sus tareas pasan una parte significativa del tiempo esperando la E / S de red. Sin embargo, para las tareas de compilación, este no es el caso.
ivan_pozdeev
30

Yo, personalmente, uso make -j ndonde n es "número de núcleos" + 1.

Sin embargo, no puedo dar una explicación científica: he visto a muchas personas que usan la misma configuración y me han dado muy buenos resultados hasta ahora.

De todos modos, debe tener cuidado porque algunas cadenas de fabricación simplemente no son compatibles con la --jobsopción y pueden dar lugar a resultados inesperados. Si experimenta errores de dependencia extraños, intente makesin ellos --jobs.

ereOn
fuente
19
La explicación (aunque no puedo dar fe de su carácter científico) es que "+ 1" da un trabajo adicional que se ejecuta mientras cualquiera de los otros n trabajos está haciendo E / S.
Laurynas Biveinis
@LaurynasBiveinis: Pero los trabajos se ejecutan en diferentes núcleos todo el tiempo, al menos con más frecuencia que con un entorno más conservador donde un trabajo tiene la oportunidad de permanecer en el mismo núcleo durante un período de tiempo más largo. Hay pros y contras aquí ...
krlmlr
1
El número de núcleos + 1 también es mi configuración predeterminada. Un problema es que, en cualquier sistema razonablemente grande, make parece retrasar la vinculación y realizar todos los pasos de vinculación juntos. En este punto te quedas sin RAM. ¡Bah!
bobbogo
4
algunas cadenas de creación simplemente no son compatibles con la opción --jobs -> Esto significa que le faltan dependencias. Arregle sus archivos MAKE si alguna vez obtiene esto.
dascandy
7

En última instancia, tendrá que hacer algunos puntos de referencia para determinar el mejor número para usar en su compilación, pero recuerde que la CPU no es el único recurso que importa.

Si tiene una compilación que depende en gran medida del disco, por ejemplo, entonces generar muchos trabajos en un sistema multinúcleo podría ser más lento , ya que el disco tendrá que hacer un trabajo adicional moviendo el cabezal del disco hacia adelante y hacia atrás para servir a todos los diferentes trabajos (dependiendo de muchos factores, como qué tan bien el sistema operativo maneja la caché del disco, el soporte de la cola de comandos nativa por el disco, etc.).

Y luego tienes núcleos "reales" frente a hiperprocesos. Puede que se beneficie o no de generar trabajos para cada hiperproceso. Nuevamente, tendrá que comparar para averiguarlo.

No puedo decir que haya probado específicamente #cores + 1 , pero en nuestros sistemas (Intel i7 940, 4 núcleos con hyperthreaded, mucha RAM y unidades VelociRaptor) y nuestra compilación (compilación de C ++ a gran escala que es alternativamente CPU y yo / O límite) hay muy poca diferencia entre -j4 y -j8. (Es quizás un 15% mejor ... pero ni siquiera el doble de bueno).

Si me voy a almorzar, usaré -j8, pero si quiero usar mi sistema para cualquier otra cosa mientras se está construyendo, usaré un número menor. :)

ijprest
fuente
1
Parece genial, pero estoy confundido por qué no tomarías ese + 15% cada vez usando-j 8
SG
1
@sg: j8 fue realmente agotador para el sistema que describí en mi publicación original ... la máquina aún se podía usar , pero definitivamente respondía menos. Entonces, si aún quisiera usarlo de forma interactiva para otras tareas (normalmente trabajando en otro código, y tal vez en la compilación ocasional de una sola DLL), reservaría un par de núcleos para los bits interactivos.
ijprest
@sg: Esto es un problema menor en nuestros sistemas más nuevos ... sospecho que se debe principalmente a que ahora estamos ejecutando SSD. (Creo que ahora que vamos a SSDs estamos completamente ligados a la CPU ... intentamos construir completamente en una unidad RAM sin casi ninguna mejora). haciendo algo más que una simple edición de texto en primer plano.
ijprest
5

Acabo de recibir un procesador Athlon II X2 Regor con un Foxconn M / B y 4 GB de memoria G-Skill.

Puse mi 'cat / proc / cpuinfo' y 'free' al final de esto para que otros puedan ver mis especificaciones. Es un Athlon II x2 de doble núcleo con 4 GB de RAM.

uname -a on default slackware 14.0 kernel is 3.2.45.

Descargué la fuente del kernel del siguiente paso (linux-3.2.46) en / archive4;

extraído ( tar -xjvf linux-3.2.46.tar.bz2);

cd en el directorio ( cd linux-3.2.46);

y copió la configuración predeterminada del kernel sobre ( cp /usr/src/linux/.config .);

utilizado make oldconfigpara preparar la configuración del kernel 3.2.46;

luego ejecutó make con varios encantamientos de -jX.

Probé los tiempos de cada ejecución emitiendo make después del comando time, por ejemplo, 'time make -j2'. Entre cada ejecución, 'rm -rf' el árbol linux-3.2.46 y lo extraje de nuevo, copié el /usr/src/linux/.config predeterminado en el directorio, ejecuté make oldconfig y luego hice mi prueba 'make -jX' nuevamente .

simple "hacer":

real    51m47.510s
user    47m52.228s
sys     3m44.985s
bob@Moses:/archive4/linux-3.2.46$

como arriba pero con make -j2

real    27m3.194s
user    48m5.135s
sys     3m39.431s
bob@Moses:/archive4/linux-3.2.46$

como arriba pero con make -j3

real    27m30.203s
user    48m43.821s
sys     3m42.309s
bob@Moses:/archive4/linux-3.2.46$

como arriba pero con make -j4

real    27m32.023s
user    49m18.328s
sys     3m43.765s
bob@Moses:/archive4/linux-3.2.46$

como arriba pero con make -j8

real    28m28.112s
user    50m34.445s
sys     3m49.877s
bob@Moses:/archive4/linux-3.2.46$

'cat / proc / cpuinfo' produce:

bob@Moses:/archive4$ cat /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 16
model           : 6
model name      : AMD Athlon(tm) II X2 270 Processor
stepping        : 3
microcode       : 0x10000c8
cpu MHz         : 3399.957
cache size      : 1024 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 2
apicid          : 0
initial apicid  : 0
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmo
v pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rd
tscp lm 3dnowext 3dnow constant_tsc nonstop_tsc extd_apicid pni monitor cx16 p
opcnt lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowpre
fetch osvw ibs skinit wdt npt lbrv svm_lock nrip_save
bogomips        : 6799.91
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management: ts ttp tm stc 100mhzsteps hwpstate

processor       : 1
vendor_id       : AuthenticAMD
cpu family      : 16
model           : 6
model name      : AMD Athlon(tm) II X2 270 Processor
stepping        : 3
microcode       : 0x10000c8
cpu MHz         : 3399.957
cache size      : 1024 KB
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2
apicid          : 1
initial apicid  : 1
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmo
v pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rd
tscp lm 3dnowext 3dnow constant_tsc nonstop_tsc extd_apicid pni monitor cx16 p
opcnt lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowpre
fetch osvw ibs skinit wdt npt lbrv svm_lock nrip_save
bogomips        : 6799.94
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management: ts ttp tm stc 100mhzsteps hwpstate

rendimientos 'libres':

bob@Moses:/archive4$ free
             total       used       free     shared    buffers     cached
Mem:       3991304    3834564     156740          0     519220    2515308
sloMoses
fuente
1
¿Qué hace make -jen ese sistema? Se supone que Make debe verificar la carga y escalar el número de procesos en función de la carga.
docwhat
1
make -jno limita la cantidad de trabajos en absoluto. Esto suele ser desastroso en un proyecto de tamaño mediano o grande, ya que rápidamente se bifurcan más trabajos de los que puede soportar la RAM. La opción que necesita restringir por carga es -l [load], junto con-j
Matt G
5

Ambos no se equivocan. Para estar en paz consigo mismo y con el autor del software que está compilando (se aplican diferentes restricciones de subprocesos múltiples / subprocesos únicos a nivel de software), le sugiero que utilice:

make -j`nproc`

Notas: nproces un comando de Linux que devolverá el número de núcleos / subprocesos (CPU moderna) disponibles en el sistema. Si lo coloca debajo de ticks `como arriba, el número pasará al comando make.

Información adicional: como alguien mencionó, el uso de todos los núcleos / subprocesos para compilar software puede literalmente estrangular su caja hasta casi morir (no responde) e incluso puede llevar más tiempo que usar menos núcleos. Como vi a un usuario de Slackware publicado aquí, tenía CPU de doble núcleo pero aún proporcionó pruebas hasta j 8, que dejó de ser diferente en j 2 (solo 2 núcleos de hardware que la CPU puede utilizar). Entonces, para evitar que el cuadro no responda, le sugiero que lo ejecute así:

make -j`nproc --ignore=2`

Esto pasará la salida de nproca makey restar 2 núcleos de su resultado.

Lucifer digital
fuente
3

Solo como un ref:

De la Spawning Multiple Build Jobssección en LKD :

donde n es el número de trabajos a generar. La práctica habitual es generar uno o dos trabajos por procesador. Por ejemplo, en una máquina de procesador dual, se podría hacer

$ hacer j4

Nan Xiao
fuente
enlace roto, ¿es esta cita de Desarrollo del núcleo de Linux de Robert Love?
Behrooz
Sí, es de ese libro.
Nan Xiao
1

Según mi experiencia, debe haber algunos beneficios de rendimiento al agregar trabajos adicionales. Es simplemente porque la E / S de disco es uno de los cuellos de botella además de la CPU. Sin embargo, no es fácil decidir el número de trabajos adicionales, ya que está muy interconectado con el número de núcleos y tipos de disco que se utilizan.

Mate
fuente
1

Muchos años después, la mayoría de estas respuestas siguen siendo correctas. Sin embargo, ha habido un pequeño cambio: usar más trabajos de los que tiene núcleos físicos ahora da una aceleración realmente significativa. Como apéndice a la tabla de Dascandy, estos son mis momentos para compilar un proyecto en un AMD Ryzen 5 3600X en Linux. (The Powder Toy, comete c6f653ac3cef03acfbc44e8f29f11e1b301f1ca2)

Le recomiendo que se revise usted mismo, pero he descubierto con comentarios de otros que usar su conteo de núcleos lógicos para el conteo de trabajos funciona bien en Zen. Junto a eso, el sistema no parece perder capacidad de respuesta. Me imagino que esto también se aplica a las CPU Intel recientes. Tenga en cuenta que también tengo un SSD, por lo que puede valer la pena probar su CPU usted mismo.

scons -j1 --release --native  120.68s user 9.78s system 99% cpu 2:10.60 total
scons -j2 --release --native  122.96s user 9.59s system 197% cpu 1:07.15 total
scons -j3 --release --native  125.62s user 9.75s system 292% cpu 46.291 total
scons -j4 --release --native  128.26s user 10.41s system 385% cpu 35.971 total
scons -j5 --release --native  133.73s user 10.33s system 476% cpu 30.241 total
scons -j6 --release --native  144.10s user 11.24s system 564% cpu 27.510 total
scons -j7 --release --native  153.64s user 11.61s system 653% cpu 25.297 total
scons -j8 --release --native  161.91s user 12.04s system 742% cpu 23.440 total
scons -j9 --release --native  169.09s user 12.38s system 827% cpu 21.923 total
scons -j10 --release --native  176.63s user 12.70s system 910% cpu 20.788 total
scons -j11 --release --native  184.57s user 13.18s system 989% cpu 19.976 total
scons -j12 --release --native  192.13s user 14.33s system 1055% cpu 19.553 total
scons -j13 --release --native  193.27s user 14.01s system 1052% cpu 19.698 total
scons -j14 --release --native  193.62s user 13.85s system 1076% cpu 19.270 total
scons -j15 --release --native  195.20s user 13.53s system 1056% cpu 19.755 total
scons -j16 --release --native  195.11s user 13.81s system 1060% cpu 19.692 total
( -jinf test not included, as it is not supported by scons.)

Pruebas realizadas en Ubuntu 19.10 con Ryzen 5 3600X, Samsung 860 Evo SSD (SATA) y 32 GB de RAM

Nota final: otras personas con un 3600X pueden obtener mejores tiempos que yo. Al hacer esta prueba, tenía habilitado el modo Eco, reduciendo un poco la velocidad de la CPU.

moonheart08
fuente
0

¡SI! En mi 3950x, ejecuto -j32 y me ahorra horas de tiempo de compilación. Todavía puedo ver youtube, navegar por la web, etc. durante la compilación sin ninguna diferencia. El procesador no siempre está vinculado incluso con un nvme 970 PRO de 1TB o un nvme Auros Gen4 de 1TB y 64GB de 3200C14. Incluso cuando lo es, no me doy cuenta de la interfaz de usuario. Planeo probar con -j48 en un futuro cercano en algunos grandes proyectos futuros. Espero, como probablemente lo haga, ver una mejora impresionante. Es posible que aquellos que todavía tengan un quad-core no obtengan las mismas ganancias ...

El propio Linus acaba de actualizar a 3970x y puedes apostar tu último dólar, al menos está ejecutando -j64.

lazyacevw
fuente