2009-10-14 10 views
47

Odkąd dowiedziałem się o -j, użyłem -j8 beztrosko. Któregoś dnia kompilowałem instalację atlasu, a wykonanie nie powiodło się. W końcu wyśledziłem to wszystko, co było zrobione w porządku - i wszystko poszło dobrze, kiedy wróciłem do singlethreaded. To mnie denerwuje. Jakiego rodzaju warunków muszę przestrzegać podczas pisania własnych plików make, aby uniknąć zrobienia czegoś nieoczekiwanego w make -j?GNU make -j opcja

+4

Czy jesteś pewien, że to wina zrobić? Pisanie poprawnych plików Makefile jest podatne na błędy. –

+1

Meh, nawet pliki Makefile generowane przez autotools są błędne. Spróbuj skompilować GCC z opcją -j2 lub wyższą. – LiraNuna

Odpowiedz

36

Myślę, że make-j będzie szanował zależności określone w pliku Makefile; tj. jeśli podasz, że objA zależy od objB i objC, make nie zacznie działać na objA do momentu zakończenia objB i objC.

Najprawdopodobniej twój plik Makefile nie określa dokładnie wymaganej kolejności operacji, a to tylko szczęście, że zadziała w twoim przypadku w przypadku pojedynczego wątku.

+10

Zgadza się. Pracuję na bazie kodu około 20 milionów linii, głównie w C z małym C++. Jest podzielony na setki komponentów, z których około połowa używa make, a połowa z nich używa dżemu. Zawsze robię równoległe kompilacje z opcją -j; w przeciwnym razie kompilacje zajęłyby wiele godzin. Dżem generuje własne zależności, więc komponenty, które z niego korzystają, zawsze osiągają sukces. Ale komponenty używające ręcznie tworzonych plików makefile dławią się, niezmiennie z powodu nieodpowiednich zależności. –

+0

Wiele widziałem w Internecie, że '-j' odnosi się do liczby procesorów, z którymi chcesz się skompilować. Przeprowadziłem test na mojej 8-rdzeniowej maszynie i 'make' działa aż do' -j8' włącznie (poza tym jest to 100% użycie na wszystkich rdzeniach, podobne do '-j8'). Przy każdym zwiększeniu liczby całkowitej widzę, że kolejne użycie rdzenia wzrasta o około * 100% *. Czy uważasz, że jest to dobra zasada, aby wiedzieć, co określić jako wartość '-j'? – Jacksonkr

+0

Jedyną dobrą zasadą, jaką znam, jest wykonanie "make clean; time make -jn" w twoim programie, z różnymi wartościami (n), i zobaczenie, ile czasu zajmuje każda kompilacja, a następnie idź z dowolnym ustawieniem ukończonym w najmniej czasu. Istnieje zbyt wiele zmiennych (szybkość procesora, szybkość pamięci RAM, rozmiar pamięci RAM, prędkość dysku twardego, obciążenie procesu systemu operacyjnego itp.), Aby uzyskać przydatne uogólnienia. –

22

W skrócie - upewnij się, że zależności są poprawne i kompletne.

Jeśli korzystasz z gwintu pojedynczego, możesz ślepo ignorować ukryte zależności między celami. Używając równoległego, nie można polegać na niejawnych zależnościach. Wszystkie one powinny być wyraźne. Jest to prawdopodobnie najczęstsza pułapka. Zwłaszcza jeśli używasz celów .phony jako zależności.

This link jest dobrym podkładem przy niektórych problemach z równoległą marką.

+0

+1 Dla dobrego linku. Teraz czuję, że mogę również zaufać make-j i jak rozwiązywać problemy, kiedy się pojawiają. Warto przeczytać. –

7

Jeśli masz markę rekurencyjną, rzeczy mogą się łatwo złamać. Jeśli nie robisz rekursywnej marki, to dopóki twoje zależności są poprawne i kompletne, nie powinieneś napotkać żadnych problemów (z wyjątkiem błędu w make). Zobacz Recursive Make Considered Harmful, aby uzyskać bardziej szczegółowy opis problemów z rekurencyjną marką.

8

Oto przykład problemu, który napotkałem, gdy zacząłem używać równoległych kompilacji. Mam cel o nazwie "świeży", którego używam do odbudowy celu od zera ("świeża" kompilacja). W przeszłości kodowałem "świeży" cel, po prostu wskazując "czyste", a następnie "buduj" jako zależności.

build: ## builds the default target 
clean: ## removes generated files 
fresh: clean build ## works for -j1 but fails for -j2 

To działało w porządku, dopóki nie zaczął używać równolegle buduje, ale równolegle buduje, próbuje zrobić zarówno „czyste” i „budować” jednocześnie. Zmieniłem definicję "świeżej" w następujący sposób, aby zagwarantować prawidłową kolejność operacji.

fresh: 
    $(MAKE) clean 
    $(MAKE) build 

Jest to zasadniczo kwestia prawidłowego określenia zależności. Sztuczka polega na tym, że równoległe kompilacje są w tym przypadku bardziej restrykcyjne niż kompilacje jednowątkowe. Mój przykład pokazuje, że lista zależności dla danego celu nie musi oznaczać kolejności wykonania.

+2

Robota rekursywna, yuk !. Prawidłowy sposób mówienia, aby _please zawsze czyścić przed build_ to oczywiście 'build: clean'. – bobbogo

+1

@bobbogo: Nie chcę zawsze czyścić przed kompilacją - w większości przypadków jest to niepotrzebne. "Świeży" cel, który opisałem, jest po prostu zwykłym skryptem, który uruchamia "make clean", a następnie "make build" (w tych rzadkich momentach, kiedy chcę to zrobić). Nie widzę żadnej szkody w wykonywaniu rekursywnie w tym scenariuszu. – nobar

+2

Jak o ochronie go warunkowego: 'ifeq ($ (MAKECMDGOALS), fresh); kompilacja: czysta; endif' (zastąp średniki nowymi liniami)? – eriktous

1

Dobrze jest mieć zautomatyzowany test, aby przetestować opcję -j WSZYSTKICH plików make. Nawet najlepsi programiści mają problemy z opcją -j make. Najczęstsze problemy są najprostsze.

myrule: subrule1 subrule2 
    echo done 

subrule1: 
    echo hello 

subrule2: 
    echo world 

W zwykłej wersji zobaczysz cześć -> świat -> zrobione. Z make -j 4 możesz zobaczyć świat -> cześć -> zrobione

Tam, gdzie to widzę, większość to tworzenie katalogów wyjściowych.Na przykład:

build: $(DIRS) $(OBJECTS) 
    echo done 

$(DIRS): 
    [email protected] -p [email protected] 

$(OBJECTS): 
    $(CC) ... 
0

Pomyślałem, że dodaję odpowiedź do podsekcji, ponieważ nie pokazuje ona wyraźnie efektu. Dodaje się jednak kilka poleceń dotyczących trybu uśpienia. Działa na Linuksie.

następnie uruchomić make pokazuje różnice z:

  • zrobić
  • zrobić -j4

all: toprule1 

toprule1: botrule2 subrule1 subrule2 
    @echo toprule 1 start 
    @sleep 0.01 
    @echo toprule 1 done 

subrule1: botrule1 
    @echo subrule 1 start 
    @sleep 0.08 
    @echo subrule 1 done 

subrule2: botrule1 
    @echo subrule 2 start 
    @sleep 0.05 
    @echo subrule 2 done 

botrule1: 
    @echo botrule 1 start 
    @sleep 0.20 
    @echo "botrule 1 done (good prerequiste in sub)" 

botrule2: 
    @echo "botrule 2 start" 
    @sleep 0.30 
    @echo "botrule 2 done (bad prerequiste in top)" 
Powiązane problemy