GNU make: Sollte die Anzahl der Jobs der Anzahl der CPU-Kerne in einem System entsprechen?

86

Es scheint einige Kontroversen darüber zu geben, ob die Anzahl der Jobs in GNU make der Anzahl der Kerne entsprechen soll oder ob Sie die Erstellungszeit optimieren können, indem Sie einen zusätzlichen Job hinzufügen, der in die Warteschlange gestellt werden kann, während die anderen "arbeiten". .

Ist es besser zu nutzen -j4oder -j5ein Quad - Core - System auf?

Haben Sie ein Benchmarking gesehen (oder durchgeführt), das das eine oder andere unterstützt?

Johan
quelle
8
Nur für den Tipp, können Sie verwenden make `nproc`, um CPU-unabhängiges Skript zu machen :)
VivienG
Wenn Sie eine Mischung aus Rezepten haben, die an Io und CPU gebunden sind, werden Sie möglicherweise viel mehr als NCPUs wollen. Erwägen Sie auch das Hinzufügen von -lX-Optionen. Dies ist keine wirklich beantwortbare Frage, außer "es hängt von Ihrer Hardware ab und macht Aufgaben."
James Moore
Eine Verbesserung ist technisch möglich. Sie benötigen eine langsame Festplatte, nicht genügend RAM und viele kleine Quellcodedateien. Vor einem Jahrzehnt leichter zu bekommen.
Hans Passant

Antworten:

56

Ich würde sagen, das Beste, was Sie tun können, ist, es selbst an Ihrer speziellen Umgebung und Arbeitsbelastung zu messen. Es scheint, dass es zu viele Variablen gibt (Größe / Anzahl der Quelldateien, verfügbarer Speicher, Festplatten-Caching, ob sich Ihr Quellverzeichnis und Ihre Systemheader auf verschiedenen Festplatten befinden usw.), um eine einheitliche Antwort zu erhalten.

Meine persönliche Erfahrung (auf einem 2-Core-MacBook Pro) ist, dass -j2 deutlich schneller als -j1 ist, aber darüber hinaus (-j3, -j4 usw.) gibt es keine messbare Beschleunigung. Für meine Umgebung scheint "jobs == Anzahl der Kerne" eine gute Antwort zu sein. (YMMV)

David Gelhar
quelle
56

Ich habe mein Heimprojekt auf meinem 4-Core mit Hyperthreading-Laptop ausgeführt und die Ergebnisse aufgezeichnet. Dies ist ein ziemlich compilerlastiges Projekt, das jedoch einen Unit-Test von 17,7 Sekunden am Ende beinhaltet. Die Kompilierungen sind nicht sehr E / A-intensiv; Es ist sehr viel Speicher verfügbar, und wenn nicht, befindet sich der Rest auf einer schnellen SSD.

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

Grundlegende Ergebnisse:

  • Die Skalierung auf die Kernanzahl erhöht die Leistung nahezu linear. Die Echtzeit ging von 2,5 Minuten auf 1,0 Minuten zurück (2,5-mal so schnell), aber die beim Kompilieren benötigte Zeit stieg von 2,11 auf 2,50 Minuten. Das System bemerkte kaum eine zusätzliche Belastung in diesem Bit.
  • Durch die Skalierung von der Kernanzahl auf die Threadanzahl wurde die Benutzerlast immens von 2,50 Minuten auf 4,38 Minuten erhöht. Diese nahezu Verdoppelung ist höchstwahrscheinlich darauf zurückzuführen, dass die anderen Compilerinstanzen gleichzeitig dieselben CPU-Ressourcen verwenden wollten. Das System wird ein bisschen mehr mit Anfragen und Aufgabenwechseln belastet, was dazu führt, dass die benötigte Zeit 17,7 Sekunden beträgt. Der Vorteil liegt bei etwa 6,5 ​​Sekunden bei einer Kompilierungszeit von 53,5 Sekunden, was einer Beschleunigung von 12% entspricht.
  • Die Skalierung von der Thread-Anzahl auf die doppelte Thread-Anzahl ergab keine signifikante Beschleunigung. Die Zeiten bei 12 und 15 sind höchstwahrscheinlich statistische Anomalien, die Sie ignorieren können. Die Gesamtzeit nimmt geringfügig zu, ebenso wie die Systemzeit. Beides ist höchstwahrscheinlich auf einen erhöhten Taskwechsel zurückzuführen. Dies hat keinen Nutzen.

Meine Vermutung im Moment: Wenn Sie etwas anderes auf Ihrem Computer tun, verwenden Sie die Kernanzahl. Wenn Sie dies nicht tun, verwenden Sie die Thread-Anzahl. Ein Überschreiten zeigt keinen Nutzen. Irgendwann werden sie speicherbeschränkt und kollabieren dadurch, was das Kompilieren viel langsamer macht. Die "inf" -Linie wurde zu einem viel späteren Zeitpunkt hinzugefügt, was mir den Verdacht gab, dass es für die 8+ Jobs eine thermische Drosselung gab. Dies zeigt, dass für diese Projektgröße keine Speicher- oder Durchsatzbeschränkung gilt. Es ist jedoch ein kleines Projekt, da 8 GB Speicher zum Kompilieren vorhanden sind.

dascandy
quelle
Laut stackoverflow.com/questions/56272639/… können Sie einen Vorteil erzielen , wenn Sie mehr Aufgaben ausführen als CPUs, aber nur, wenn Ihre Aufgaben einen erheblichen Teil der Zeit damit verbringen, auf Netzwerk-E / A zu warten. Bei Kompilierungsaufgaben ist dies jedoch nicht der Fall.
ivan_pozdeev
30

Ich persönlich verwende, make -j nwobei n "Anzahl der Kerne" + 1 ist.

Ich kann jedoch keine wissenschaftliche Erklärung abgeben: Ich habe viele Leute gesehen, die dieselben Einstellungen verwendeten, und sie haben mir bisher ziemlich gute Ergebnisse geliefert.

Auf jeden Fall müssen Sie vorsichtig sein, da einige Make-Chains einfach nicht mit der --jobsOption kompatibel sind und zu unerwarteten Ergebnissen führen können. Wenn Sie seltsame Abhängigkeitsfehler haben, versuchen Sie es einfach makeohne --jobs.

ereOn
quelle
19
Die Erklärung (kann jedoch nicht für seine Wissenschaftlichkeit bürgen) ist, dass "+ 1" einen zusätzlichen Job ergibt, der ausgeführt wird, während einer der anderen n Jobs E / A ausführt.
Laurynas Biveinis
@LaurynasBiveinis: Aber dann werden die Jobs die ganze Zeit auf verschiedenen Kernen ausgeführt, zumindest häufiger als in einer konservativeren Umgebung, in der ein Job die Möglichkeit erhält, über einen längeren Zeitraum auf demselben Kern zu bleiben. Hier gibt es Vor- und Nachteile ...
krlmlr
1
Die Anzahl der Kerne + 1 ist auch meine Standardeinstellung. Ein Problem ist, dass in jedem relativ großen System make die Verknüpfung zu verzögern scheint und alle Verknüpfungsschritte zusammen ausführt. Zu diesem Zeitpunkt geht Ihnen der Arbeitsspeicher aus. Bah!
Bobbogo
4
Einige Make-Chains sind einfach nicht mit der Option --jobs kompatibel -> Dies bedeutet, dass Abhängigkeiten fehlen. Repariere deine Makefiles, falls du das jemals bekommst.
Dascandy
7

Letztendlich müssen Sie einige Benchmarks durchführen, um die beste Anzahl für Ihren Build zu ermitteln. Denken Sie jedoch daran, dass nicht nur die CPU von Bedeutung ist!

Wenn Sie beispielsweise einen Build haben, der stark von der Festplatte abhängt, ist das Laichen vieler Jobs auf einem Multicore-System möglicherweise langsamer , da die Festplatte zusätzliche Arbeit leisten muss, um den Festplattenkopf hin und her zu bewegen, um alle zu bedienen die verschiedenen Jobs (abhängig von vielen Faktoren, wie z. B. wie gut das Betriebssystem mit dem Festplatten-Cache umgeht, Unterstützung für native Befehlswarteschlangen durch die Festplatte usw.).

Und dann haben Sie "echte" Kerne im Vergleich zu Hyper-Threading. Sie können von Spawning-Jobs für jeden Hyper-Thread profitieren oder nicht. Auch hier müssen Sie einen Benchmark durchführen, um dies herauszufinden.

Ich kann nicht sagen, dass ich #cores + 1 speziell ausprobiert habe , aber auf unseren Systemen (Intel i7 940, 4 Hyperthread-Kerne, viel RAM und VelociRaptor-Laufwerke) und unserem Build (groß angelegter C ++ - Build, der abwechselnd CPU und ich ist / O gebunden) gibt es sehr wenig Unterschied zwischen -j4 und -j8. (Es ist vielleicht 15% besser ... aber bei weitem nicht doppelt so gut.)

Wenn ich zum Mittagessen weggehe, verwende ich -j8, aber wenn ich mein System für etwas anderes verwenden möchte, während es erstellt wird, verwende ich eine niedrigere Zahl. :) :)

ijprest
quelle
1
Scheint großartig, aber ich bin verwirrt, warum Sie diese + 15% nicht jedes Mal nehmen würden, wenn Sie-j 8
sg
1
@sg: j8 hat das System, das ich in meinem ursprünglichen Beitrag beschrieben habe, wirklich belastet ... die Maschine war noch verwendbar , aber sie reagierte definitiv weniger. Wenn ich es also weiterhin interaktiv für andere Aufgaben verwenden möchte (normalerweise für anderen Code und möglicherweise für den gelegentlichen Build einer einzelnen DLL), würde ich ein paar Kerne für die interaktiven Bits reservieren.
ijprest
@sg: Dies ist auf unseren neueren Systemen weniger problematisch ... Ich vermute, es liegt hauptsächlich daran, dass wir jetzt SSDs ausführen. (Ich denke, wir sind jetzt, da wir SSDs verwenden, vollständig an die CPU gebunden. Wir haben versucht, vollständig auf einem RAM-Laufwerk aufzubauen, ohne dass dies verbessert wurde.) Aber ich werde trotzdem ein paar Kerne frei lassen, wenn ich es bin mehr als nur einfache Textbearbeitung im Vordergrund.
ijprest
5

Ich habe gerade einen Athlon II X2 Regor Proc mit einem Foxconn M / B und 4 GB G-Skill-Speicher erhalten.

Ich setze meine 'cat / proc / cpuinfo' und 'free' am Ende, damit andere meine Spezifikationen sehen können. Es ist ein Dual-Core-Athlon II x2 mit 4 GB RAM.

uname -a on default slackware 14.0 kernel is 3.2.45.

Ich habe die Kernelquelle des nächsten Schritts (Linux-3.2.46) auf / archive4 heruntergeladen.

extrahierte es ( tar -xjvf linux-3.2.46.tar.bz2);

cd'd in das Verzeichnis ( cd linux-3.2.46);

und kopierte die Konfiguration des Standardkernels über ( cp /usr/src/linux/.config .);

wird verwendet make oldconfig, um die 3.2.46-Kernelkonfiguration vorzubereiten;

dann lief make mit verschiedenen Beschwörungen von -jX.

Ich habe die Timings jedes Laufs getestet, indem ich make nach dem Befehl time ausgegeben habe, z. B. 'time make -j2'. Zwischen jedem Lauf habe ich den Linux-3.2.46-Baum 'rm -rf' erstellt und erneut extrahiert, die Standardeinstellung /usr/src/linux/.config in das Verzeichnis kopiert, make oldconfig ausgeführt und dann meinen 'make -jX'-Test erneut durchgeführt .

einfach "machen":

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

wie oben, aber mit make -j2

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

wie oben, aber mit make -j3

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

wie oben, aber mit make -j4

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

wie oben, aber mit make -j8

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

'cat / proc / cpuinfo' ergibt:

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

"freie" Erträge:

bob@Moses:/archive4$ free
             total       used       free     shared    buffers     cached
Mem:       3991304    3834564     156740          0     519220    2515308
sloMoses
quelle
1
Was macht gerade make -jauf diesem System? Make soll die Last überprüfen und die Anzahl der Prozesse basierend auf der Last skalieren.
docwhat
1
make -jbegrenzt die Anzahl der Jobs überhaupt nicht. Dies ist bei einem mittelgroßen oder großen Projekt normalerweise katastrophal, da schnell mehr Jobs gegabelt werden, als vom RAM unterstützt werden können. Die Option, die Sie durch Last einschränken müssen, ist -l [load]in Verbindung mit-j
Matt G
5

Beides ist nicht falsch. Um mit sich selbst und dem Autor der Software, die Sie kompilieren, in Frieden zu sein (auf Softwareebene selbst gelten verschiedene Einschränkungen für mehrere Threads / Single-Threads), empfehle ich Folgendes:

make -j`nproc`

Hinweise: nprocist ein Linux-Befehl, der die Anzahl der auf dem System verfügbaren Kerne / Threads (moderne CPU) zurückgibt. Wenn Sie es wie oben unter Häkchen setzen, wird die Nummer an den Befehl make übergeben.

Zusätzliche Informationen: Wie bereits erwähnt, kann die Verwendung aller Kerne / Threads zum Kompilieren von Software Ihre Box buchstäblich fast zum Erliegen bringen (da sie nicht reagiert) und möglicherweise sogar länger dauern als die Verwendung weniger Kerne. Wie ich hier gesehen habe, hatte ein Slackware-Benutzer eine Dual-Core-CPU, lieferte aber immer noch Tests bis zu j 8, die bei j 2 nicht mehr anders waren (nur 2 Hardware-Kerne, die die CPU verwenden kann). Um zu vermeiden, dass die Box nicht reagiert, schlage ich vor, dass Sie sie wie folgt ausführen:

make -j`nproc --ignore=2`

Dadurch wird die Ausgabe von nprocan übergeben makeund 2 Kerne von ihrem Ergebnis subtrahiert.

Digital Lucifer
quelle
3

Nur als Ref:

Aus dem Spawning Multiple Build JobsAbschnitt in LKD :

Dabei ist n die Anzahl der Jobs, die erzeugt werden sollen. Üblicherweise werden ein oder zwei Jobs pro Prozessor erzeugt. Auf einem Dual-Prozessor-Computer könnte dies beispielsweise der Fall sein

$ make j4

Nan Xiao
quelle
defekter Link, ist dieses Zitat aus der Linux-Kernel-Entwicklung von Robert Love?
Behrooz
Ja, es ist aus diesem Buch.
Nan Xiao
1

Aus meiner Erfahrung muss es einige Leistungsvorteile geben, wenn zusätzliche Jobs hinzugefügt werden. Dies liegt einfach daran, dass die Festplatten-E / A neben der CPU einer der Engpässe ist. Es ist jedoch nicht einfach, die Anzahl der zusätzlichen Jobs zu bestimmen, da diese stark mit der Anzahl der Kerne und Typen der verwendeten Festplatte verbunden sind.

Matt
quelle
1

Viele Jahre später sind die meisten dieser Antworten immer noch richtig. Es hat sich jedoch ein wenig geändert: Wenn Sie mehr Jobs als physische Kerne verwenden, wird dies jetzt erheblich beschleunigt. Als Ergänzung zu Dascandys Tabelle sind hier meine Zeiten für das Kompilieren eines Projekts auf einem AMD Ryzen 5 3600X unter Linux. (The Powder Toy, Commit c6f653ac3cef03acfbc44e8f29f11e1b301f1ca2)

Ich empfehle, sich selbst zu überprüfen, aber ich habe mit Beiträgen anderer festgestellt, dass die Verwendung Ihrer logischen Kernanzahl für die Jobanzahl unter Zen gut funktioniert. Daneben scheint das System nicht an Reaktionsfähigkeit zu verlieren. Ich kann mir vorstellen, dass dies auch für neuere Intel-CPUs gilt. Beachten Sie, dass ich auch eine SSD habe, sodass es sich möglicherweise lohnt, Ihre CPU selbst zu testen.

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.)

Tests unter Ubuntu 19.10 mit Ryzen 5 3600X, Samsung 860 Evo SSD (SATA) und 32 GB RAM

Schlussbemerkung: Andere Leute mit einem 3600X haben möglicherweise bessere Zeiten als ich. Bei diesem Test war der Eco-Modus aktiviert, wodurch die Geschwindigkeit der CPU etwas verringert wurde.

Mondherz08
quelle
0

JA! Auf meinem 3950x starte ich -j32 und es spart Stunden Kompilierungszeit! Ich kann während des Kompilierens ohne Unterschied immer noch YouTube ansehen, im Internet surfen usw. Der Prozessor ist auch bei einem 1 TB 970 PRO nvme oder 1 TB Auros Gen4 nvme und 64 GB 3200C14 nicht immer gebunden. Selbst wenn es so ist, bemerke ich die Benutzeroberfläche nicht. Ich plane, in naher Zukunft einige große anstehende Projekte mit -j48 zu testen. Ich erwarte, wie Sie wahrscheinlich tun, eine beeindruckende Verbesserung. Diejenigen, die noch einen Quad-Core haben, erhalten möglicherweise nicht die gleichen Gewinne ...

Linus selbst hat gerade ein Upgrade auf ein 3970x durchgeführt und Sie können Ihren unteren Dollar wetten, er läuft mindestens -j64.

Lazyacevw
quelle