Ninja: Ninja consomme toute la mémoire disponible

Créé le 27 mai 2018  ·  20Commentaires  ·  Source: ninja-build/ninja

J'ai enquêté sur les causes de l'échange sur mon système et je suis tombé sur ce fragment de code ninja : https://github.com/ninja-build/ninja/blob/03df526e07b62c0c5dfe61720cf9263ae4fb808b/src/ninja.cc#L223 -L233

Ninja est utilisé par le système de construction Android, et comme je compile beaucoup de code Android, ses performances affectent fortement la convivialité de mon système.

Mon PC de travail a un processeur à 4 cœurs avec un maximum de 8 threads, et le PC à la maison a un processeur à 8 cœurs avec un maximum de 16 (!!) threads. Les deux ont 8 Go de RAM.

Inutile de dire que les compilations ninja accumulent rapidement toute la mémoire disponible et provoquent de lourds échanges.

À l'heure actuelle, ninja alloue par défaut les threads CPU + 2, ce qui peut facilement épuiser les ressources du système d'exploitation, si la quantité de mémoire disponible ne "correspond" pas au nombre de processeurs. Il existe peu d'autres programmes avec ce type de défaut, mais la plupart d'entre eux sont des jeux, qui sont optimisés pour gérer les immobilisations et économiser de la mémoire. Ninja traite des données externes - code source du logiciel, - dont certaines sont très gourmandes en mémoire (par exemple C++). Ce n'est certainement PAS correct. Si la tendance actuelle des processeurs se poursuit, nous verrons bientôt des ordinateurs destinés aux consommateurs avec plus de 64 cœurs. Si la tendance actuelle de la RAM se poursuit, la plupart de ces ordinateurs n'auront pas la même quantité de RAM.

J'ai vu des discussions sur la conservation de la mémoire, utilisée par la compilation, en surveillant dynamiquement l'utilisation de la mémoire. Je ne m'en soucie pas personnellement - la plupart de mes projets ont une empreinte de compilation prévisible.

Au lieu de cela, j'aimerais que ninja effectue quelques vérifications de base et limite son parallélisme maximal, en fonction de la mémoire système disponible. Si certains processeurs installés n'ont pas au moins 1 Go de RAM chacun, ne comptez pas ces processeurs dans le paramètre de parallélisme par défaut. Cela maintiendra le nombre de tâches parallèles à peu près le même pour la plupart des systèmes avec moins de 4 processeurs ainsi que pour les serveurs de build Xeon d'entreprise, tout en fournissant une valeur par défaut plus raisonnable pour les systèmes avec une quantité de RAM inférieure à la moyenne.

Commentaire le plus utile

@jimon

Je comprends que vous vouliez aider, mais ce n'est pas le but.

Mon point:

1) make a des valeurs par défaut sûres et vous permet de passer à un plus grand parallélisme via une variable d'environnement.
2) ninja a des valeurs par défaut non sécurisées. J'ai constamment peur que la construction d'un programme fasse planter mon système (je ne veux pas vérifier quel système de construction est utilisé par chaque logiciel AUR aléatoire, avant de le construire !) Je ne sais pas comment changer globalement les valeurs par défaut de ninja.

Tous les 20 commentaires

ninja ne connaît pas le processus qu'il exécute, je pense également que le modèle qu'un processus utilise un cœur de processeur semble le mieux adapté à la plupart des bâtiments.
Mais la consommation de mémoire est largement différente entre les commandes (par exemple, un simple script python par rapport à la liaison d'objets volumineux), et il est difficile d'avoir une hypothèse commune sur l'utilisation de la mémoire pour de telles commandes.

Si vous souhaitez contrôler le parallélisme du processus consommateur de mémoire, il est préférable de spécifier un -j inférieur ou d'utiliser la fonctionnalité de pool lorsque vous connaissez bien l'empreinte mémoire de votre build.
https://ninja-build.org/manual.html#ref_pool

Je pense que le modèle qu'un processus utilise un cœur de processeur semble le mieux adapté à la plupart des bâtiments.

Je crois que cette quantité totale de RAM devrait également être prise en compte. Le passage au disque dur n'aide certainement pas les performances réelles.

Si vous souhaitez contrôler le parallélisme du processus consommateur de mémoire, il est préférable de spécifier un -j inférieur ou d'utiliser la fonctionnalité de pool

Je ne suis pas développeur du système de construction Android NDK, donc je ne transmets pas d'arguments de ligne de commande à la commande ninja, — le système de construction Android le fait.

J'ai défini la variable d'environnement MAKEFLAGS="-j4" (qui est respectée par la plupart des systèmes de construction basés sur make ), mais ninja ne semble pas l'utiliser.

Je suppose que vous pouvez fournir des arguments à ninja directement via cmake à partir de gradle. D'après ce que je peux voir, l'intégration cmake dans gradle a un champ arguments . Peut-être que cela fonctionnera?

@jimon

Je comprends que vous vouliez aider, mais ce n'est pas le but.

Mon point:

1) make a des valeurs par défaut sûres et vous permet de passer à un plus grand parallélisme via une variable d'environnement.
2) ninja a des valeurs par défaut non sécurisées. J'ai constamment peur que la construction d'un programme fasse planter mon système (je ne veux pas vérifier quel système de construction est utilisé par chaque logiciel AUR aléatoire, avant de le construire !) Je ne sais pas comment changer globalement les valeurs par défaut de ninja.

@Alexander-- , imaginez que vous avez un seul exécutable (compilateur ou similaire) qui alloue simplement plus de RAM que vous n'en avez (via un fichier d'échange ou d'autres moyens). Vous avez un système de construction qui exécute simplement cette application et cet alto - votre système n'est pas réactif. Par cet exemple mental, il est évident que ce problème n'est pas résoluble dans l'absolu. Essayons maintenant d'imaginer un cas où l'exécutable alloue progressivement de la mémoire au fil du temps, ninja n'a aucune connaissance préalable de l'utilisation de la mémoire, et il deviendra évident que la RAM n'est plus là qu'après qu'elle est réellement partie, à ce stade, il n'y a rien à faire. Le problème n'est pas trivialement résoluble, et je ne pense pas qu'il devrait être dans la portée du projet.

En ce qui concerne make ayant des valeurs par défaut plus sûres - je ne peux pas imaginer combien de temps de développement est perdu simplement parce que les développeurs ne sont pas conscients ou ne peuvent pas être dérangés par la définition du parallélisme dans make. Je vois constamment mes collègues perdre des minutes voire des heures parce qu'ils ne sont pas conscients que les builds se font dans un thread par défaut.

Je ne veux pas m'opposer à la résolution de votre problème, et je ne veux pas paraître dur :) Mais je pense que la solution la plus optimale est simplement de remplacer le comportement progressif et de passer à autre chose. Parce qu'il est très spécifique à votre projet/ordinateur et n'apparaît probablement pas à plus grande échelle.

Quel système de build Android utilisez-vous ? Construire des applications Android avec le NDK, ou construire l'intégralité de la plate-forme (ROM) ?

Ninja + Gradle + Jack = Enfer. (il prend au moins 16 Go de RAM)
Je ne sais pas exactement où se situe le problème. mais je suppose que le problème majeur vient de la construction de Java en utilisant jack-server. La chose la plus importante est que Ninja (de Soong) change toutes les choses héritées et introduit tant de petites choses logicielles, donc nous sommes perdus dans le système de construction et ne pouvons pas trouver le problème et la solution.
J'ai raté l'ancien Make. s'il vous plaît, sortez-moi de cet enfer de construction.
Je devrais revenir pour résoudre ce "Aucun serveur Jack en cours d'exécution. Essayez 'jack-admin start-server'"

Pour reformuler @atetubou : si votre CPU a 64 cœurs, il est raisonnable de supposer qu'il est capable d'exécuter 64 programmes en parallèle. La raison pour laquelle cette hypothèse est un problème pour vous est que vos tâches de compilation semblent être plus volumineuses qu'un programme ordinaire. Ninja dispose d'un mécanisme pour communiquer ces informations, via la fonction "pool". Si vous lisez cette section de documentation, vous verrez qu'elle est conçue exactement pour ce problème. https://ninja-build.org/manual.html#ref_pool

Je comprends votre problème mais je ne vois pas de moyen facile pour Ninja de le résoudre. Vous dites qu'il devrait "considérer" la quantité totale de RAM, mais quelle sorte de formule pourrions-nous utiliser ?

La prise @ av930 disparaît dans P et peut être désactivée dans les versions O. J'ai supprimé l'utilisation de jack des versions O et mon 8 cœurs avec 12 Go de mémoire n'a aucun problème à fonctionner avec plus de 16 threads. Vous pouvez également modifier les arguments utilisés par ninja lors des constructions. Le problème, c'est que Jack n'est pas ninja.

De plus, ce changement pourrait vous aider : https://github.com/ninja-build/ninja/pull/1399

Oh mon Dieu! merci, ça marche. Enfin, j'ai terminé la construction complète d'AOSP dans une machine 16GRAM.
jack désactiver : ANDROID_COMPILE_WITH_JACK := false dans build/make/core/javac.mk

c'est le journal.
[ 99% 101694/101695] Installer l'image fs du système : out/target/product/taimen/system.img
out/target/product/taimen/system.img+ maxsize=2740531200 blocksize=135168 total=1076289888 reserve=27709440
[100 % 101695/101695] Image vbmeta cible : out/target/product/taimen/vbmeta.img

construction terminée avec succès (04:43:31 (hh:mm:ss))

Je pense que cela pourrait être résolu en disant au système d'exploitation que pour un groupe de processus en cours d'exécution n , il est acceptable de suspendre jusqu'à n-1 processus dans une situation de mémoire insuffisante. Est-ce que quelqu'un sait si cela peut être réalisé avec des cgroups sous Linux ?

Il existe en fait deux PR qui implémentent une limite de mémoire : #1354 et #660. La discussion dans ce dernier est intéressante.

Cela ressemble surtout à la construction d'Android qui tenait le ninja mal en ne mettant pas Jack dans une piscine.

@Nico

Pourriez-vous indiquer une version spécifique de Ninja, qui a résolu ce problème ? Je vois toujours Ninja utiliser trop de tâches parallèles par défaut chaque fois que je construis quelque chose à partir d'AUR. Il semble que Ninja tente toujours de créer 18 threads sur une machine à 16 cœurs, quelle que soit la quantité de RAM disponible.

Ninja ne fait rien ici, c'est au générateur de s'assurer que les choses qui ont besoin de beaucoup de RAM (ou similaire) sont dans un pool de taille appropriée.

c'est au générateur de s'assurer que les choses qui ont besoin de beaucoup de RAM (ou similaire) sont dans un pool de taille appropriée

Est-ce votre opinion personnelle ou la position officielle des développeurs Ninja ? La documentation ne parle jamais d'une telle responsabilité, et je n'ai pas entendu parler de générateurs, qui se mêlent en fait du parallélisme Ninja.

Quoi qu'il en soit, les auteurs de générateurs ne sont pas mieux placés que vous pour choisir le nombre de processus parallèles à exécuter. C'est mieux décidé par l'administrateur de buildserver.

Vous dites qu'il n'y a pas (et ne devrait jamais y avoir) de moyen générique de réduire l'utilisation des ressources Ninja, qui fonctionne quel que soit le générateur utilisé. Je ne suis pas d'accord. Il existe des outils de construction (par exemple GNU Make), qui ont cette fonctionnalité, il existe donc déjà un précédent pour une telle fonctionnalité.

La documentation ne parle jamais d'une telle responsabilité, et je n'ai pas entendu parler de générateurs, qui se mêlent en fait du parallélisme Ninja.

Pas explicitement sur la RAM, mais :

"personnalisation de la construction au moment de la construction. Les options appartiennent au programme qui génère les fichiers ninja."

"Ninja n'a presque pas de fonctionnalités ; juste celles nécessaires pour obtenir des versions correctes tout en donnant le plus de complexité à la génération des fichiers d'entrée ninja."

Vous dites qu'il n'y a pas (et ne devrait jamais y avoir) de moyen générique de réduire l'utilisation des ressources Ninja, qui fonctionne quel que soit le générateur utilisé.

Il y a deux façons : le drapeau -j et le drapeau -l .

Je vois toujours Ninja utiliser trop de tâches parallèles par défaut chaque fois que je construis quelque chose à partir d'AUR.

Je ne sais pas exactement comment fonctionne l'AUR, mais est-ce que mettre le script suivant dans /usr/local/bin et le rendre exécutable via chmod +x /usr/local/bin/ninja fonctionnerait ?

#!/usr/bin/env python3

import sys
import subprocess

try:
    subprocess.check_call(['/usr/bin/ninja', '-j1'] + sys.argv[1:])
except subprocess.CalledProcessError as e:
    exit(e.returncode)

-j1 aurait pu être mentionné comme dans la première réponse.
J'ai ce projet qui a acquis au fil du temps beaucoup de projets externes externes. Autrement dit, ils se construisent de manière statique au lieu de la construction dynamique utilisée par le makefile commun, de sorte qu'ils ont chacun leurs propres drapeaux et sources qui sont construits.
Aujourd'hui, sur cet ordinateur portable plus ancien (je n'ai jamais utilisé ninja sur ce système), il ne s'agit que d'un système Windows 7 de 4 Go avec Core i7 (8 threads). Lorsque j'ai commencé la construction, tout le système a exploré pendant une demi-heure pendant qu'il échangeait vers et depuis le disque.
Cela a entraîné des erreurs de baizaare

C:/general/build/mingw64-x64/sack/RelWithDebInfo_out/core/include/SACK/stdhdrs.h:259:24: fatal error: C:/general/build/mingw64-x64/sack/RelWithDebInfo_out/core/include/SACK/loadsock.h: Invalid argument
compilation terminated.

pourquoi l'inclusion d'un fichier est-elle un argument invalide ? le fichier existe....
donc toute la construction est terminée, mais n'a pas construit beaucoup de cibles avec succès ...
même quand j'utilise maintenant

ninja -j1 -v install 

les processus ninja internes utilisent toujours un -j8

La construction a fini par être -j8 sur le premier qui a lancé 8 projets externes chacun avec -j8, donc -j64 a atteint un pic de mémoire jusqu'à ce qu'il n'y en ait plus ...

Le support de Jobserver pourrait aider dans ce cas : #1139

La réimplémentation indépendante du langage de construction Ninja https://github.com/michaelforney/samurai prend en charge cela via la variable d'environnement SAMUFLAGS. Utilisez simplement samu partout où vous utiliseriez autrement ninja, ou installez samu comme votre système /usr/bin/ninja (certaines distributions Linux ont des options pour installer cette implémentation concurrente comme ninja par défaut ou uniquement).

Je conseille de mettre à jour les packages AUR qui se construisent avec ninja, pour utiliser community/samurai à la place.

Cette page vous a été utile?
0 / 5 - 0 notes