Depuis que j'ai entendu parler de -j, j'utilise allègrement -j8. L'autre jour, je compilais une installation d'atlas et la marque a échoué. Finalement, je l'ai trouvé jusqu'à ce que les choses soient rendues irrecevables - et cela a bien fonctionné une fois que je suis retourné à la marque à filetage unique. Cela me rend nerveux. Quelles conditions dois-je surveiller lors de l'écriture de mes propres fichiers make pour éviter de faire quelque chose d'inattendu avec make -j?
Je pense que make -j respectera les dépendances que vous spécifiez dans votre Makefile; c'est-à-dire si vous spécifiez que objA dépend de objB et objC, alors make ne commencera pas à travailler sur objA tant que objB et objC ne seront pas terminés.
Il est fort probable que votre Makefile ne spécifie pas suffisamment l'ordre des opérations nécessaire, et c'est juste une chance que cela fonctionne pour vous dans le cas d'un seul thread.
En bref - assurez-vous que vos dépendances sont correctes et complètes.
Si vous utilisez une marque unique, vous pouvez ignorer aveuglément les dépendances implicites entre les cibles. Lors de l'utilisation de make parallèle, vous ne pouvez pas compter sur les dépendances implicites. Ils devraient tous être explicités. C'est probablement le piège le plus courant. Surtout si vous utilisez des cibles .phony comme dépendances.
This link est une bonne introduction à certains des problèmes avec la marque parallèle.
Voici un exemple d'un problème que j'ai rencontré lorsque j'ai commencé à utiliser des versions parallèles. J'ai une cible appelée "fraîche" que j'utilise pour reconstruire la cible à partir de zéro (une construction "fraîche"). Dans le passé, j'ai codé la cible "fraîche" en indiquant simplement "nettoyer" puis "construire" comme dépendances.
build: ## builds the default target
clean: ## removes generated files
fresh: clean build ## works for -j1 but fails for -j2
Cela a bien fonctionné jusqu'à ce que je commence à utiliser des builds parallèles, mais avec des builds parallèles, il essaie de faire à la fois "clean" et "build" simultanément. J'ai donc changé la définition de "frais" comme suit afin de garantir le bon ordre des opérations.
fresh:
$(MAKE) clean
$(MAKE) build
Il s'agit fondamentalement simplement de spécifier correctement les dépendances. L'astuce est que les builds parallèles sont plus stricts à ce sujet que les builds à un seul thread. Mon exemple montre qu'une liste de dépendances pour une cible donnée n'indique pas nécessairement l'ordre d'exécution.
Si vous avez une marque récursive, les choses peuvent se casser assez facilement. Si vous ne faites pas de make récursif, tant que vos dépendances sont correctes et complètes, vous ne devriez pas rencontrer de problèmes (sauf pour un bug dans make). Voir Marque récursive considérée comme nuisible pour une description beaucoup plus approfondie des problèmes de la marque récursive.
C'est une bonne idée d'avoir un test automatisé pour tester l'option -j de TOUS les fichiers make. Même les meilleurs développeurs ont des problèmes avec l'option -j de make. Les problèmes les plus courants sont les plus simples.
myrule: subrule1 subrule2
echo done
subrule1:
echo hello
subrule2:
echo world
En mode normal, vous verrez bonjour -> monde -> fait. Avec make -j 4, vous pourrez voir le monde -> bonjour -> fait
Là où j'ai vu cela se produire le plus, c'est avec la création de répertoires de sortie. Par exemple:
build: $(DIRS) $(OBJECTS)
echo done
$(DIRS):
-@mkdir -p $@
$(OBJECTS):
$(CC) ...
J'ai juste pensé que j'ajouterais à la réponse de subsetbrew car elle ne montre pas clairement l'effet. Cependant, l'ajout de certaines commandes de veille le fait. Eh bien, cela fonctionne sur Linux.
Ensuite, exécuter make montre des différences avec:
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)"