GNU make: czy liczba zadań powinna być równa liczbie rdzeni procesora w systemie?


87

Wydaje się, że istnieją pewne kontrowersje co do tego, czy liczba zadań w make GNU ma być równa liczbie rdzeni, czy też można zoptymalizować czas kompilacji, dodając jedno dodatkowe zadanie, które można ustawić w kolejce, podczas gdy inne „działają” .

Czy lepiej jest użyć -j4lub -j5na systemie czterordzeniowy?

Czy widziałeś (lub zrobiłeś) jakieś testy porównawcze, które obsługują jedną lub drugą?


8
Na przykład, możesz użyć make `nproc`do stworzenia skryptu niezależnego od procesora :)
VivienG,

Jeśli masz mieszankę recept, które są powiązane z io i z procesorem, to potencjalnie będziesz potrzebować znacznie więcej niż jednostek NCPU. Rozważ także dodanie opcji -lX. To naprawdę nie jest pytanie, na które można odpowiedzieć, inne niż „to zależy od sprzętu i tworzenia zadań”.
James Moore,

Poprawa jest technicznie możliwa. Potrzebujesz wolnego dysku, za mało pamięci RAM i wielu małych plików z kodem źródłowym. Łatwiej o dziesięć lat temu.
Hans Passant

Odpowiedzi:


56

Powiedziałbym, że najlepszą rzeczą do zrobienia jest porównanie go samodzielnie w konkretnym środowisku i obciążeniu pracą. Wygląda na to, że istnieje zbyt wiele zmiennych (rozmiar / liczba plików źródłowych, dostępna pamięć, pamięć podręczna dysku, czy katalog źródłowy i nagłówki systemowe znajdują się na różnych dyskach itp.), Aby uzyskać jedną odpowiedź dla wszystkich.

Moje osobiste doświadczenie (na 2-rdzeniowym MacBooku Pro) jest takie, że -j2 jest znacznie szybsze niż -j1, ale poza tym (-j3, -j4 itd.) Nie ma mierzalnego przyspieszenia. Tak więc dla mojego środowiska „praca == liczba rdzeni” wydaje się być dobrą odpowiedzią. (YMMV)


57

Uruchomiłem projekt domowy na moim 4-rdzeniowym laptopie z technologią hyperthreading i zapisałem wyniki. Jest to projekt dość obciążający kompilator, ale zawiera na końcu test jednostkowy trwający 17,7 sekundy. Kompilacje nie są zbyt intensywne we / wy; jest bardzo dużo dostępnej pamięci, a jeśli nie, reszta jest na szybkim dysku 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

Podstawowe wyniki:

  • Skalowanie do liczby rdzeni zwiększa wydajność prawie liniowo. Czas rzeczywisty skrócił się z 2,5 minuty do 1,0 minuty (2,5 razy szybciej), ale czas kompilacji wzrósł z 2,11 do 2,50 minuty. System nie zauważył prawie żadnego dodatkowego obciążenia w tym bicie.
  • Skalowanie od liczby rdzeni do liczby wątków znacznie zwiększyło obciążenie użytkownika, z 2,50 minuty do 4,38 minuty. To prawie podwojenie jest najprawdopodobniej spowodowane tym, że inne instancje kompilatora chciały używać tych samych zasobów procesora w tym samym czasie. System jest nieco bardziej obciążony żądaniami i przełączaniem zadań, przez co zużywa 17,7 sekundy. Zaletą jest około 6,5 sekundy przy czasie kompilacji wynoszącym 53,5 sekundy, co daje 12% przyspieszenie.
  • Skalowanie od liczby wątków do podwójnej liczby wątków nie dało znaczącego przyspieszenia. Czasy w wieku 12 i 15 lat to najprawdopodobniej anomalie statystyczne, które można zignorować. Całkowity czas, jaki zajmuje, wzrasta nieznacznie, podobnie jak czas systemowy. Oba są najprawdopodobniej spowodowane częstszym przełączaniem zadań. Nie ma z tego żadnej korzyści.

Domyślam się teraz: jeśli robisz coś innego na swoim komputerze, użyj liczby rdzeni. Jeśli nie, użyj liczby wątków. Przekroczenie tego nie przynosi żadnych korzyści. W pewnym momencie staną się ograniczone w pamięci i z tego powodu zapadną się, co znacznie spowolni kompilację. Wiersz „inf” został dodany znacznie później, co dało mi podejrzenie, że w 8+ zadaniach było jakieś dławienie termiczne. To pokazuje, że dla tego rozmiaru projektu nie obowiązuje żaden limit pamięci ani przepustowości. Jest to jednak mały projekt, biorąc pod uwagę 8 GB pamięci do wkompilowania.


Według stackoverflow.com/questions/56272639/… , możesz uzyskać przewagę wykonując więcej zadań niż masz procesory, ale tylko wtedy, gdy twoje zadania spędzają znaczną część czasu czekając na sieciowe I / O. W przypadku zadań kompilacji tak nie jest.
ivan_pozdeev

30

Osobiście używam make -j ngdzie n to „liczba rdzeni” + 1.

Nie mogę jednak podać naukowego wyjaśnienia: widziałem wiele osób korzystających z tych samych ustawień i jak dotąd dały mi całkiem niezłe wyniki.

W każdym razie musisz być ostrożny, ponieważ niektóre łańcuchy produkcji po prostu nie są zgodne z tą --jobsopcją i mogą prowadzić do nieoczekiwanych wyników. Jeśli doświadczasz dziwnych błędów zależności, po prostu spróbuj makebez --jobs.


19
Wyjaśnienie (nie można jednak ręczyć za jego naukową jakość) jest takie, że „+ 1” daje dodatkowe zadanie, które działa, podczas gdy którekolwiek z pozostałych n zadań wykonuje operacje we / wy.
Laurynas Biveinis

@LaurynasBiveinis: Ale wtedy zadania są wykonywane przez cały czas na różnych rdzeniach, przynajmniej częściej niż w bardziej konserwatywnym środowisku, w którym praca ma szansę pozostać na tym samym rdzeniu przez dłuższy czas. Są tu wady i zalety ...
krlmlr

1
Liczba rdzeni + 1 jest również moim domyślnym ustawieniem. Jedną z kwestii jest to, że w każdym rozsądnie dużym systemie make wydaje się opóźniać łączenie i wykonywać wszystkie czynności łączące razem. W tym momencie zabrakło pamięci RAM. Bah!
bobbogo

4
niektóre łańcuchy make po prostu nie są zgodne z opcją --jobs -> Oznacza to, że brakuje Ci zależności. Napraw swoje makefile, jeśli kiedykolwiek to dostaniesz.
dascandy

7

Ostatecznie będziesz musiał wykonać kilka testów porównawczych, aby określić najlepszą liczbę do użycia w swojej kompilacji, ale pamiętaj, że procesor nie jest jedynym zasobem, który ma znaczenie!

Jeśli masz kompilację, która w dużym stopniu opiera się na dysku, na przykład tworzenie wielu zadań w systemie wielordzeniowym może być w rzeczywistości wolniejsze , ponieważ dysk będzie musiał wykonać dodatkową pracę, przesuwając głowicę dysku w przód iw tył, aby obsłużyć wszystkie różne zadania (w zależności od wielu czynników, takich jak to, jak dobrze system operacyjny obsługuje pamięć podręczną dysku, natywna obsługa kolejkowania poleceń przez dysk itp.).

A potem mamy „prawdziwe” rdzenie w porównaniu z hiperwątkowością. Możesz lub nie skorzystać na spawnowaniu zadań dla każdego hiperwątku. Ponownie, będziesz musiał wykonać test porównawczy, aby się dowiedzieć.

Nie mogę powiedzieć, że specjalnie wypróbowałem #cores + 1 , ale w naszych systemach (Intel i7 940, 4 rdzenie hyperthreaded, dużo pamięci RAM i dyski VelociRaptor) i naszej kompilacji (kompilacja C ++ na dużą skalę, na przemian z procesorem i I / O bound) jest bardzo mała różnica między -j4 i -j8. (Może o 15% lepiej ... ale nigdzie nie jest nawet dwa razy tak dobre).

Jeśli wyjeżdżam na lunch, użyję -j8, ale jeśli chcę użyć mojego systemu do czegokolwiek innego podczas tworzenia, użyję niższej liczby. :)


1
Wydaje się świetne, ale jestem zdezorientowany, dlaczego nie wziąłbyś tego + 15% za każdym razem, używając-j 8
sg

1
@sg: J8 naprawdę opodatkowania w systemie opisałem w moim oryginalnym poście ... maszyna była nadal użyteczny , ale to było zdecydowanie mniej elastyczne. Więc gdybym nadal chciał używać go interaktywnie do innych zadań (zazwyczaj pracując na innym kodzie i być może sporadycznie przy kompilacji pojedynczej biblioteki DLL), zarezerwowałbym kilka rdzeni na bity interaktywne.
ijprest

@sg: To mniejszy problem w naszych nowszych systemach ... Podejrzewam, że głównie dlatego, że teraz używamy dysków SSD. (Myślę, że teraz, gdy idziemy na dyski SSD, jesteśmy całkowicie związani z procesorem ... próbowaliśmy zbudować całkowicie na dysku RAM bez prawie żadnych ulepszeń.) Ale nadal zostawiam kilka wolnych rdzeni, jeśli będę robi coś więcej niż prostą edycję tekstu na pierwszym planie.
ijprest

5

Właśnie dostałem procesor Athlon II X2 Regor z Foxconn M / B i 4 GB pamięci G-Skill.

Na końcu umieszczam moje „cat / proc / cpuinfo” i „free”, aby inni mogli zobaczyć moje specyfikacje. To dwurdzeniowy Athlon II x2 z 4 GB pamięci RAM.

uname -a on default slackware 14.0 kernel is 3.2.45.

Pobrałem źródło jądra następnego kroku (linux-3.2.46) do / archive4;

wyodrębniono ( tar -xjvf linux-3.2.46.tar.bz2);

cd do katalogu ( cd linux-3.2.46);

i skopiowałem domyślną konfigurację jądra przez ( cp /usr/src/linux/.config .);

używany make oldconfigdo przygotowania konfiguracji jądra 3.2.46;

następnie uruchomiono make z różnymi inkantacjami -jX.

Sprawdziłem czasy każdego uruchomienia, wydając polecenie make po czasie, np. „Time make -j2”. Pomiędzy każdym uruchomieniem 'rm -rf' drzewo linux-3.2.46 i wyodrębniłem je ponownie, skopiowałem domyślny /usr/src/linux/.config do katalogu, uruchomiłem make oldconfig i ponownie wykonałem test 'make -jX' .

zwykły „marka”:

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

jak wyżej, ale z make -j2

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

jak wyżej, ale z make -j3

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

jak wyżej, ale z make -j4

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

jak wyżej, ale z make -j8

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

„cat / proc / cpuinfo” daje:

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

„darmowe” plony:

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

1
Co po prostu make -jrobi w tym systemie? Make ma na celu sprawdzenie obciążenia i skalowanie liczby procesów na podstawie obciążenia.
docwhat

1
make -jw ogóle nie ogranicza liczby zleceń. Jest to zwykle katastrofalne w przypadku projektów o średnim lub dużym rozmiarze, ponieważ szybko rozwidla się więcej zadań, niż może być obsługiwanych przez pamięć RAM. Opcja, którą musisz ograniczyć ze względu na obciążenie, to -l [load]w połączeniu z-j
Matt G

5

Obie się nie mylą. Aby być w zgodzie ze sobą i autorem kompilowanego oprogramowania (na samym poziomie oprogramowania obowiązują różne ograniczenia wielowątkowe / jednowątkowe), sugeruję użycie:

make -j`nproc`

Uwagi: nprocto polecenie linux, które zwróci liczbę rdzeni / wątków (nowoczesny procesor) dostępnych w systemie. Umieszczenie go pod znacznikami `jak powyżej, przekaże numer do polecenia make.

Dodatkowe informacje: Jak ktoś wspomniał, użycie wszystkich rdzeni / wątków do kompilacji oprogramowania może dosłownie zapchać komputer niemal do śmierci (brak odpowiedzi), a nawet może zająć więcej czasu niż użycie mniejszej liczby rdzeni. Jak widziałem, jeden użytkownik Slackware napisał tutaj, że ma dwurdzeniowy procesor, ale nadal zapewniał testy do j 8, które przestały być inne na j 2 (tylko 2 rdzenie sprzętowe, które CPU może wykorzystać). Aby uniknąć braku odpowiedzi, sugeruję uruchomienie go w następujący sposób:

make -j`nproc --ignore=2`

Spowoduje to przekazanie wyniku nprocdo makei odjęcie 2 rdzeni od wyniku.


3

Po prostu jako ref:

Z Spawning Multiple Build Jobssekcji w LKD :

gdzie n to liczba zadań do odrodzenia. Zwykłą praktyką jest tworzenie jednego lub dwóch zadań na procesor. Na przykład na komputerze z dwoma procesorami można to zrobić

$ make j4


uszkodzony link, czy to cytat z Linux Kernel Development autorstwa Roberta Love?
Behrooz

Tak, to z tej książki.
Nan Xiao

1

Z mojego doświadczenia wynika, że ​​przy dodawaniu dodatkowych miejsc pracy muszą istnieć pewne korzyści związane z wydajnością. Dzieje się tak po prostu dlatego, że dyskowe operacje we / wy są jedną z wąskich gardeł obok procesora. Jednak nie jest łatwo zdecydować o liczbie dodatkowych zadań, ponieważ jest to silnie powiązane z liczbą rdzeni i typami używanego dysku.


1

Wiele lat później większość z tych odpowiedzi jest wciąż poprawna. Jednak nastąpiła pewna zmiana: użycie większej liczby zadań niż rdzeni fizycznych daje teraz naprawdę znaczące przyspieszenie. Jako dodatek do tabeli Dascandy, oto moje czasy kompilacji projektu na AMD Ryzen 5 3600X w systemie Linux. (The Powder Toy, commit c6f653ac3cef03acfbc44e8f29f11e1b301f1ca2)

Zalecam sprawdzenie siebie, ale na podstawie opinii innych odkryłem, że używanie logicznej liczby rdzeni do liczenia zadań działa dobrze w Zen. Poza tym system nie wydaje się tracić czasu reakcji. Wyobrażam sobie, że dotyczy to również ostatnich procesorów Intela. Zwróć uwagę, że mam również dysk SSD, więc może warto samemu przetestować procesor.

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

Testy przeprowadzone na Ubuntu 19.10 z Ryzen 5 3600X, Samsung 860 Evo SSD (SATA) i 32 GB RAM

Uwaga końcowa: inne osoby z 3600X mogą mieć lepsze czasy niż ja. Podczas tego testu miałem włączony tryb Eco, zmniejszając nieco prędkość procesora.


0

TAK! Na moim 3950x uruchamiam -j32 i oszczędza to godziny kompilacji! Nadal mogę oglądać youtube, przeglądać internet itp. Podczas kompilacji bez żadnej różnicy. Procesor nie zawsze jest powiązany, nawet z 1 TB 970 PRO nvme lub 1 TB Auros Gen4 NVMe i 64 GB 3200C14. Nawet jeśli tak jest, nie zauważam, że interfejs użytkownika jest mądry. Planuję testować z -j48 w najbliższej przyszłości w niektórych dużych nadchodzących projektach. Spodziewam się, podobnie jak Ty, pewnej imponującej poprawy. Ci, którzy nadal mają czterordzeniowy procesor, mogą nie uzyskać takich samych korzyści ...

Sam Linus właśnie podwyższył poziom do 3970x i możesz postawić swojego dolnego dolara, on ma co najmniej -j64.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.