web-dev-qa-db-fra.com

GNU make: le nombre de travaux doit-il être égal au nombre de cœurs CPU dans un système?

Il semble y avoir une certaine controverse quant à savoir si le nombre de travaux dans GNU make est censé être égal au nombre de cœurs, ou si vous pouvez optimiser le temps de construction en ajoutant un travail supplémentaire qui peut être mis en file d'attente pendant que les autres "travaillent".

Est-il préférable d'utiliser -j4 ou -j5 sur un système quad core?

Avez-vous vu (ou fait) des analyses comparatives qui soutiennent l'un ou l'autre?

78
Johan

Je dirais que la meilleure chose à faire est de le comparer vous-même à votre environnement et à votre charge de travail. Il semble qu'il y ait trop de variables (taille/nombre de fichiers source, mémoire disponible, mise en cache du disque, si votre répertoire source et les en-têtes du système sont situés sur différents disques, etc.) pour une réponse unique.

Mon expérience personnelle (sur un MacBook Pro à 2 cœurs) est que -j2 est nettement plus rapide que -j1, mais au-delà (-j3, -j4 etc.), il n'y a pas d'accélération mesurable. Donc pour mon environnement, "jobs == nombre de cœurs" semble être une bonne réponse. (YMMV)

52
David Gelhar

J'ai exécuté mon projet personnel sur mon ordinateur à 4 coeurs avec hyperthreading et enregistré les résultats. C'est un projet assez lourd à compiler mais il inclut un test unitaire de 17,7 secondes à la fin. Les compilations ne sont pas très IO intensive; il y a beaucoup de mémoire disponible et sinon le reste est sur un SSD rapide.

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

Résultats de base:

  • La mise à l'échelle du nombre de cœurs augmente les performances de manière presque linéaire. Le temps réel est passé de 2,5 minutes à 1,0 minute (2,5 fois plus rapide), mais le temps pris lors de la compilation est passé de 2,11 à 2,50 minutes. Le système n'a remarqué pratiquement aucune charge supplémentaire dans ce bit.
  • La mise à l'échelle du nombre de cœurs au nombre de threads a considérablement augmenté la charge utilisateur, de 2,50 minutes à 4,38 minutes. Ce doublement est probablement dû au fait que les autres instances du compilateur souhaitaient utiliser les mêmes ressources CPU en même temps. Le système est un peu plus chargé de demandes et de changement de tâches, ce qui lui fait passer à 17,7 secondes de temps utilisé. L'avantage est d'environ 6,5 secondes sur un temps de compilation de 53,5 secondes, ce qui permet une accélération de 12%.
  • La mise à l'échelle du nombre de threads vers le double nombre de threads n'a donné aucune accélération significative. Les temps à 12 et 15 sont les anomalies statistiques les plus probables que vous pouvez ignorer. Le temps total pris augmente très légèrement, tout comme le temps système. Les deux sont probablement dus à un changement de tâche accru. Il n'y a aucun avantage à cela.

Ma conjecture en ce moment: si vous faites autre chose sur votre ordinateur, utilisez le nombre de cœurs. Si ce n'est pas le cas, utilisez le nombre de threads. Le dépasser ne montre aucun avantage. À un moment donné, ils deviendront mémoire limitée et s'effondreront à cause de cela, ce qui rend la compilation beaucoup plus lente. La ligne "inf" a été ajoutée à une date beaucoup plus tardive, ce qui me fait soupçonner qu'il y avait un étranglement thermique pour les 8+ travaux. Cela montre que pour cette taille de projet, il n'y a pas de limite de mémoire ou de débit en vigueur. C'est un petit projet, avec 8 Go de mémoire à compiler.

49
dascandy

Personnellement, j'utilise make -j n où n est "nombre de cœurs" + 1.

Je ne peux cependant pas donner d'explication scientifique: j'ai vu beaucoup de gens utiliser les mêmes paramètres et ils m'ont donné de très bons résultats jusqu'à présent.

Quoi qu'il en soit, vous devez être prudent car certaines chaînes de création ne sont tout simplement pas compatibles avec le --jobs option, et peut conduire à des résultats inattendus. Si vous rencontrez des erreurs de dépendance étranges, essayez simplement de make sans --jobs.

29
ereOn

En fin de compte, vous devrez faire quelques repères pour déterminer le meilleur nombre à utiliser pour votre build, mais n'oubliez pas que le CPU n'est pas la seule ressource qui compte!

Si vous avez une version qui dépend fortement du disque, par exemple, la génération de nombreuses tâches sur un système multicœur peut en fait être plus lente, car le disque devra faire un travail supplémentaire pour déplacer le disque aller et venir pour servir toutes les différentes tâches (en fonction de nombreux facteurs, comme la façon dont le système d'exploitation gère le cache de disque, la prise en charge de la mise en file d'attente des commandes natives par le disque, etc.).

Et puis vous avez des "vrais" cœurs contre l'hyper-threading. Vous pouvez ou non bénéficier des tâches de génération pour chaque hyper-thread. Encore une fois, vous devrez comparer pour le savoir.

Je ne peux pas dire que j'ai spécifiquement essayé # cores + 1, mais sur nos systèmes (Intel i7 940, 4 cœurs hyperthreadés, beaucoup de RAM et disques VelociRaptor) et notre build (à grande échelle Version C++ qui est alternativement liée au CPU et aux E/S), il y a très peu de différence entre -j4 et -j8. (C'est peut-être 15% mieux ... mais loin d'être deux fois plus bon.)

Si je pars pour le déjeuner, j'utiliserai -j8, mais si je veux utiliser mon système pour autre chose pendant la construction, j'utiliserai un nombre inférieur. :)

7
ijprest

Je viens de recevoir un processeur Athlon II X2 Regor avec un Foxconn M/B et 4 Go de mémoire G-Skill.

J'ai mis mon 'cat/proc/cpuinfo' et 'free' à la fin de ceci pour que les autres puissent voir mes spécifications. Il s'agit d'un Athlon II x2 double cœur avec 4 Go de RAM.

uname -a on default slackware 14.0 kernel is 3.2.45.

J'ai téléchargé la source du noyau de l'étape suivante (linux-3.2.46) dans/archive4;

extrait (tar -xjvf linux-3.2.46.tar.bz2);

cd'd dans le répertoire (cd linux-3.2.46);

et copié la configuration du noyau par défaut sur (cp /usr/src/linux/.config .);

utilisé make oldconfig pour préparer la configuration du noyau 3.2.46;

puis exécuté make avec diverses incantations de -jX.

J'ai testé les temps de chaque exécution en émettant make après la commande time, par exemple, 'time make -j2'. Entre chaque exécution, je 'rm -rf' l'arborescence linux-3.2.46 et la réextrait, copiais le /usr/src/linux/.config par défaut dans le répertoire, exécutais make oldconfig puis refaisais mon test 'make -jX' .

"make" simple:

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

comme ci-dessus mais avec make -j2

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

comme ci-dessus mais avec make -j3

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

comme ci-dessus mais avec make -j4

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

comme ci-dessus mais avec make -j8

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

'cat/proc/cpuinfo' donne:

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

rendements "gratuits":

bob@Moses:/archive4$ free
             total       used       free     shared    buffers     cached
Mem:       3991304    3834564     156740          0     519220    2515308
4
sloMoses

Tout comme une référence:

De Spawning Multiple Build Jobs section dans LKD :

où n est le nombre d'emplois à générer. La pratique habituelle consiste à générer un ou deux travaux par processeur. Par exemple, sur une machine à double processeur, on pourrait faire

$ make j4

3
Nan Xiao

Les deux n'ont pas tort. Pour être en paix avec vous-même et avec l'auteur du logiciel que vous compilez (différentes restrictions multi-thread/single-thread s'appliquent au niveau du logiciel lui-même), je vous suggère d'utiliser:

make -j`nproc`

Notes: nproc est une commande linux qui retournera le nombre de cœurs/threads (CPU modernes) disponibles sur le système. Le placer sous des ticks `comme ci-dessus passera le numéro à la commande make.

Informations supplémentaires: Comme quelqu'un l'a mentionné, l'utilisation de tous les cœurs/threads pour compiler le logiciel peut littéralement étouffer votre boîte à mort (ne répondant pas) et peut même prendre plus de temps que d'utiliser moins de cœurs. Comme j'ai vu un utilisateur Slackware ici posté, il avait un processeur dual core mais fournissait toujours des tests jusqu'à j 8, qui cessaient d'être différents en j 2 (seulement 2 cœurs matériels que le CPU peut utiliser). Donc, pour éviter une boîte qui ne répond pas, je vous suggère de l'exécuter comme ceci:

make -j`nproc --ignore=2`

Cela passera la sortie de nproc à make et soustraira 2 cœurs de son résultat.

1
Digital Lucifer

D'après mon expérience, il doit y avoir des avantages en termes de performances lors de l'ajout de travaux supplémentaires. C'est simplement parce que les E/S disque sont l'un des goulots d'étranglement en plus du CPU. Cependant, il n'est pas facile de décider du nombre de travaux supplémentaires car il est fortement interconnecté avec le nombre de cœurs et les types de disques utilisés.

1
Matt