Ninja: Possibilité d'utiliser les caractéristiques du fichier au lieu des horodatages

Créé le 14 août 2018  ·  15Commentaires  ·  Source: ninja-build/ninja

Comme indiqué dans d'autres problèmes, l'utilisation d'horodatages pour déterminer s'il faut reconstruire quelque chose peut être problématique. Bien que les horodatages soient pratiques et relativement rapides, il est souvent souhaitable de définir des décisions sur certaines caractéristiques intrinsèques au fichier lui-même, comme un hachage de son contenu.

J'utilise beaucoup git et c'est ennuyeux que le simple fait de changer de branche déclenche des reconstructions. Idéalement, je serais capable de passer de ma branche de travail actuelle à une autre branche, de ne toucher à aucun fichier, puis de revenir à la branche d'origine et de ne rien reconstruire du tout. Autant que je sache, ce n'est pas possible si le système de construction utilise des horodatages. L'utilisation de hachages de fichiers résoudrait ce problème particulier.

Je comprends que l'utilisation de hachages de fichiers ou d'autres caractéristiques de fichiers intrinsèques de ce type pourrait ralentir Ninja, donc leur utilisation devrait être une option.

feature

Commentaire le plus utile

J'ai également utilisé le hachage au travail, avec beaucoup de succès. Il est basé sur #929, mais avec un tas de correctifs, comme on peut le voir sur https://github.com/moroten/ninja/commits/hashed. hash_input = 1 sur les règles sélectionnées est très pratique. Ma branche contient toujours un bogue où les fichiers sont stat trop souvent, O(n^2) au lieu de O(n) . Le bogue est lié aux contours factices.

Un problème est de savoir comment gérer les faux bords. J'utilise des bords factices pour regrouper, par exemple, des fichiers d'en-tête. Par conséquent, mon implémentation itère de manière récursive à travers les bords factices. Cela concerne également le bogue dans # 1021.

Une autre idée est de faire du hachage un membre de première classe de ninja, c'est-à-dire de déplacer les hachages dans le journal de construction. L'utilisation de SHA256 serait une étape vers l'ajout de la prise en charge de l'API d'exécution à distance de Bazel. Une implémentation C++ peut être trouvée sur https://gitlab.com/bloomberg/recc/. Ne serait-ce pas plutôt sympa ?

Malheureusement, le tri de la sémantique pour les bords factices rompra probablement la compatibilité descendante.

Tous les 15 commentaires

929 a une implémentation. Même s'il est utilisé avec succès (dans un fork) pour des milliers de builds par jour, il n'a pas été envisagé de le fusionner.

929 est monothread et peut donc être plus lent comme ccache ou d'autres solutions. Je pense également que cela devrait être un indicateur de ligne de commande à la place, afin qu'il ne nécessite pas de modifications des définitions de construction.

Il ne peut pas (ou du moins ne devrait pas) être un indicateur de ligne de commande, car le hachage s'appliquerait alors à toutes les règles. Le hachage, par exemple, de toutes les entrées des règles de liaison est coûteux et n'est donc pas souhaité, tandis que les fichiers source et leurs dépendances connues sont de bons candidats. Pour cette distinction, cela doit faire partie de la description de construction. De plus, pour utiliser la fonctionnalité, vous devez utiliser le drapeau de manière cohérente à tout moment. Pas seulement parfois.

Réponse à l'argument mono-thread : Oui, il ajoute des instructions à la boucle mono-thread. En réalité, cela n'a d'importance que si le thread unique obtient plus de travail qu'il ne peut travailler (c'est-à-dire plus de règles se terminent que le thread peut traiter (depslog+hashlog+...)). Alors seulement, le hachage fait mal. Sinon, la boucle de thread unique attend que les travaux se terminent de toute façon. Nous n'avons jamais vu un ninja occupé avec un hachage, même avec des expériences -j1000. (Et pour les règles de finition rapide, le hachage n'est de toute façon pas intéressant pour gagner du temps.)

Considérez également : le hachage avec le hachage murmure est considérablement rapide et même les fichiers source volumineux ne prennent que quelques millisecondes pour être hachés. De plus, le hachage se produit juste après que le fichier source (et les dépendances) ont été vus par le compilateur. Par conséquent, ils sont généralement lus à partir du cache du système de fichiers.
Comme le hachage se produit pendant la construction (en parallèle aux règles exécutées), le temps de construction global n'est généralement pas affecté de manière mesurable.

Enfin, la mise en œuvre dans # 929 est opt-in et est gratuite pour les personnes qui n'utilisent pas la fonctionnalité (en plus de l'instruction if).

Le hachage, par exemple, de toutes les entrées des règles de liaison est coûteux et n'est donc pas souhaité, tandis que les fichiers source et leurs dépendances connues sont de bons candidats.

Je dirais que le hachage des entrées dans l'éditeur de liens est particulièrement souhaité car il peut souvent entraîner l'omission complète de la liaison (par exemple pour les changements de formatage ou lorsque les commentaires sont modifiés). Au fur et à mesure que la compilation des fichiers objets se termine pièce par pièce, le calcul du hachage peut se produire pendant l'exécution de la construction (comme vous l'avez souligné).

Si c'est vraiment trop lent (par exemple avec de grosses bibliothèques statiques), on pourrait penser à implémenter des hachages uniquement pour les entrées pures et non pour les fichiers intermédiaires. Cela résoudrait au moins le cas "la commutation des branches Git entraîne des reconstructions complètes".

De plus, pour utiliser la fonctionnalité, vous devez utiliser le drapeau de manière cohérente à tout moment. Pas seulement parfois.

Je dirais que c'est un avantage : si je travaille sur une seule branche et que je veux itérer rapidement, je n'utiliserais pas de hachage. Si je compare différentes branches de fonctionnalités, j'utiliserais des hachages.

De plus, pour utiliser la fonctionnalité, vous devez utiliser le drapeau de manière cohérente à tout moment. Pas seulement parfois.

Je dirais que c'est un avantage : si je travaille sur une seule branche et que je veux itérer rapidement, je n'utiliserais pas de hachage. Si je compare différentes branches de fonctionnalités, j'utiliserais des hachages.

Mais il faudrait alors un moyen de passer du non-hachage au hachage, ce qui signifie que l'état actuel des fichiers devrait être haché, de sorte que la prochaine reconstruction puisse utiliser les hachages (qui n'existaient probablement pas ou sont sortis de date si vous n'avez pas passé le drapeau).

Je suppose que le hachage utilise toujours l'horodatage en premier, de sorte que si les horodatages correspondent, il n'est pas nécessaire de comparer les hachages. Cela signifierait que les premières versions pourraient recompiler inutilement certains fichiers, mais cela ne devrait pas arriver souvent (la plupart du temps, l'heuristique d'horodatage est juste après tout).

J'ai également utilisé le hachage au travail, avec beaucoup de succès. Il est basé sur #929, mais avec un tas de correctifs, comme on peut le voir sur https://github.com/moroten/ninja/commits/hashed. hash_input = 1 sur les règles sélectionnées est très pratique. Ma branche contient toujours un bogue où les fichiers sont stat trop souvent, O(n^2) au lieu de O(n) . Le bogue est lié aux contours factices.

Un problème est de savoir comment gérer les faux bords. J'utilise des bords factices pour regrouper, par exemple, des fichiers d'en-tête. Par conséquent, mon implémentation itère de manière récursive à travers les bords factices. Cela concerne également le bogue dans # 1021.

Une autre idée est de faire du hachage un membre de première classe de ninja, c'est-à-dire de déplacer les hachages dans le journal de construction. L'utilisation de SHA256 serait une étape vers l'ajout de la prise en charge de l'API d'exécution à distance de Bazel. Une implémentation C++ peut être trouvée sur https://gitlab.com/bloomberg/recc/. Ne serait-ce pas plutôt sympa ?

Malheureusement, le tri de la sémantique pour les bords factices rompra probablement la compatibilité descendante.

Entre-temps, j'ai concocté une solution pour mon cas d'utilisation consistant à changer de branche dans Chromium (ce qui est particulièrement pénible); le petit programme Go et le script que j'utilise sont ici : https://github.com/bromite/mtool

N'hésitez pas à l'adapter à vos cas d'utilisation, si cela fonctionne pour Chromium, je parie que cela fonctionnera également pour les projets de construction plus petits (et cela prend un temps négligeable pour mes exécutions). Le seul inconvénient est que si vous utilisez le script que j'y ai publié tel quel, il mettra des fichiers .mtool partout dans chaque répertoire parent du référentiel git, mais rien qu'un gitignore global ne puisse guérir.

Il est intéressant de noter que j'utilise la sortie git ls-files --stage pour les besoins de hachage ; il est possible (mais moins efficace) de demander à git de hacher également les fichiers non indexés si votre build en dépend.

En ce qui concerne les fonctionnalités, on s'attendrait à ce que pour implémenter la fonctionnalité discutée ici, ninja puisse faire la même chose en interne (sans compter sur git) et avec des résultats de performances similaires.

Après un rapide coup d'œil, ces correctifs ninja ne semblent pas hacher la ligne de commande du compilateur en plus des fichiers d'entrée. Est-ce que je manque quelque chose?

La ligne de commande est déjà hachée par ninja et stockée dans le journal de construction.

Je suppose que le hachage utilise toujours l'horodatage en premier, de sorte que si les horodatages correspondent, il n'est pas nécessaire de comparer les hachages. Cela signifierait que les premières versions pourraient recompiler inutilement certains fichiers, mais cela ne devrait pas arriver souvent (la plupart du temps, l'heuristique d'horodatage est juste après tout).

Ce serait très mal. La comparaison des hachages peut être élidée si les horodatages ne correspondent pas ; le système de construction peut supposer que la dépendance a été modifiée, et si elle n'a pas été vraiment modifiée (seulement touchée), la construction sera sous-optimale mais correcte. Cependant, si les horodatages correspondent, il est toujours possible que la dépendance ait été modifiée et que son horodatage ait été réinitialisé de force (EDIT : ou que la cible ait été touchée et donc rendue plus récente que ses dépendances). Sans une double vérification en comparant les hachages, cela entraînerait une construction incorrecte.

Je suppose que Ninja assume déjà et saute tout le travail lorsque les horodatages correspondent. Donc, dans votre exemple, ce serait de toute façon une construction incorrecte.

Je ne connais (probablement en raison de mon ignorance) aucun outil qui produirait une sortie différente et conserverait l'horodatage précédent. Pourquoi ferait-on ça ?

À mon humble avis, sauter la vérification de hachage lorsque les horodatages correspondent est une optimisation très valable.

Le contrôle de hachage éviterait simplement certaines reconstructions "fausses sales", tout en conservant la sémantique existante déjà fournie par Ninja.

Le problème n'est pas les reconstructions faussement sales, ce sont les non-reconstructions faussement propres. Un git checkout touche tout ce qu'il écrase. Cela peut rendre une cible plus récente qu'une dépendance (oui, les gens valident le code généré pour diverses raisons valables). Une vérification de hachage empêcherait une non-reconstruction par faux nettoyage dans ce cas.

@rulatir Je pense que je comprends surtout ce que vous dites :) Je suppose que c'est l'une des raisons pour lesquelles Bazel et d'autres systèmes de construction basés sur des vérifications de hachage sont vraiment contre les sorties cibles dans l'arborescence.

Cependant, ce problème ne serait-il pas résolu si le système de construction vérifiait si les cibles sont plus récentes que l'heure précédemment connue et reconstruisait si nécessaire?

@rulatir Je pense que je comprends surtout ce que vous dites :) Je suppose que c'est l'une des raisons pour lesquelles Bazel et d'autres systèmes de construction basés sur des vérifications de hachage sont vraiment contre les sorties cibles dans l'arborescence.

Comment le hachage dépend-il de l'emplacement du fichier ?

Cependant, ce problème ne serait-il pas résolu si le système de construction vérifiait si les cibles sont plus récentes que l'heure précédemment connue et reconstruisait si nécessaire?

Je comprends que le principal avantage de l'utilisation des horodatages est d'éviter d'avoir à maintenir une base de données distincte qui garde une trace des signatures de version "précédemment connues". Si vous êtes prêt à renoncer à cet avantage, pourquoi ces signatures ne devraient-elles pas être des hachages ?

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