Outils d'analyse statique d'inclusion de fichier d'en-tête?
Un collègue m'a récemment révélé qu'un seul fichier source de la nôtre comprend plus de 3 400 en-têtes pendant la compilation. Nous avons plus de 1000 unités de traduction qui sont compilées dans une version, ce qui entraîne une énorme pénalité de performance sur les en-têtes qui ne sont sûrement pas tous utilisés.
Existe-t-il des outils d'analyse statique qui seraient en mesure de faire la lumière sur les arbres d'une telle forêt, en nous donnant spécifiquement la possibilité de décider lesquels nous devrions travailler sur l'épluchage out?
Mise à JOUR
Trouvé quelques informations intéressantes sur le coût d'inclure un fichier d'en-tête (et les types de gardes include pour optimiser son inclusion) ici, provenant de cette question.
8 réponses
La sortie de gcc -w -H <file>
peut être utile (si vous l'analysez et mettez quelques comptes) le -w
est là pour supprimer tous les Avertissements, ce qui peut être difficile à gérer.
À partir des documents gcc:
- H
Affiche le nom de chaque fichier d'en-tête utilisé, en plus des autres activités normales. Chaque nom est indenté pour montrer à quelle profondeur dans le
#include
pile, il est. Les fichiers d'en-tête précompilés sont également imprimés, même s'ils ne sont pas valides; un en-tête précompilé non valide fichier est imprimé avec...x
et valide avec...!
.
La sortie ressemble à ceci:
. /usr/include/unistd.h
.. /usr/include/features.h
... /usr/include/bits/predefs.h
... /usr/include/sys/cdefs.h
.... /usr/include/bits/wordsize.h
... /usr/include/gnu/stubs.h
.... /usr/include/bits/wordsize.h
.... /usr/include/gnu/stubs-64.h
.. /usr/include/bits/posix_opt.h
.. /usr/include/bits/environments.h
... /usr/include/bits/wordsize.h
.. /usr/include/bits/types.h
... /usr/include/bits/wordsize.h
... /usr/include/bits/typesizes.h
.. /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h
.. /usr/include/bits/confname.h
.. /usr/include/getopt.h
. /usr/include/stdio.h
.. /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h
.. /usr/include/libio.h
... /usr/include/_G_config.h
.... /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h
.... /usr/include/wchar.h
... /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stdarg.h
.. /usr/include/bits/stdio_lim.h
.. /usr/include/bits/sys_errlist.h
Multiple include guards may be useful for:
/usr/include/bits/confname.h
/usr/include/bits/environments.h
/usr/include/bits/predefs.h
/usr/include/bits/stdio_lim.h
/usr/include/bits/sys_errlist.h
/usr/include/bits/typesizes.h
/usr/include/gnu/stubs-64.h
/usr/include/gnu/stubs.h
/usr/include/wchar.h
Si vous utilisez gcc/g++, l'-M
ou -MM
option affichera une ligne avec les informations que vous cherchez. (Le premier inclura les en-têtes système alors que le second ne le fera pas. Il existe d'autres variantes; voir le manuel.)
$ gcc -M -c foo.c
foo.o: foo.c /usr/include/stdint.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
/usr/include/bits/wchar.h
Vous devez supprimer le foo.o: foo.c
Au début, mais le reste est une liste de tous les en-têtes dont dépend le fichier, il ne serait donc pas trop difficile d'écrire un script pour les rassembler et les résumer.
Bien sûr, cette suggestion est seulement utile sur Unix et seulement si personne d'autre n'a une meilleure idée. :-)
Quelques choses-
-
Utilisez "preprocess only" pour regarder la sortie de votre préprocesseur. option gcc-E, les autres compilateurs ont aussi la fonction
Utilisez des en-têtes précompilés.
-
Gcc a des options-verbose et --trace qui affichent également l'arborescence include complète, MSVC a l'option / showIncludes trouvée sous la page de propriétés C++ Avancée
En outre, affiche la hiérarchie # include pour un fichier C++ dans Visual Studio
GCC a un indicateur -M
qui affichera une liste de dépendances pour un fichier source donné. Vous pouvez utiliser ces informations pour déterminer quels fichiers ont le plus de dépendances, quels fichiers dépendent le plus, etc.
Consultez la page de manuel pour plus d'informations. Il existe plusieurs variantes de -M
.
" conception de logiciels C++ à grande échelle " par John Lakos avait des outils qui extrayaient les dépendances à la compilation entre les fichiers source.
Malheureusement, leur dépôt sur le site D'Addison-Wesley est parti (avec le site d'AW lui-même), mais j'ai trouvé une archive tar ici: http://prdownloads.sourceforge.net/introspector/LSC-rpkg-0.1.tgz?download
Je l'ai trouvé utile il y a plusieurs emplois, et il a la vertu d'être libre.
BTW, si vous n'avez pas lu le livre de Lakos, cela ressemble à votre projet en bénéficierait. (L'édition actuelle est un peu datée, mais J'entends que Lakos a un autre livre qui sortira en 2012.)
Personnellement, je ne sais pas s'il existe un outil qui dira "Supprimer ce fichier". C'est vraiment une question complexe, qui dépend de beaucoup de choses. Regarder un arbre de déclarations include va sûrement vous rendre fou.... Cela me rendrait fou, ainsi que ruiner mes yeux. Il existe de meilleures façons de faire les choses pour réduire vos temps de compilation.
- dés-inline vos méthodes de classe.
- Après les avoir désinlinées, réexaminez vos instructions include et essayez de les supprimer. Habituellement utile pour les supprimer, et de recommencer.
- préfèrent utiliser les déclarations forward sont autant que possible. Si vous désactivez les méthodes dans vos fichiers d'en-tête, vous pouvez le faire beaucoup.
- divisez les gros fichiers d'en-tête en fichiers plus petits. Si une classe dans un fichier est utilisée plus souvent que la plupart, placez-la dans un fichier d'en-tête tout seul.
- 1000 unités de traduction n'est pas vraiment beaucoup. Nous avons entre 10-20 mille. :)
- Obtenez Incredibuild si vos temps de compilation sont encore trop longs.
J'ai entendu dire qu'il y a des outils qui le font, mais je ne les utilise pas.
J'ai créé un outil https://sourceforge.net/p/headerfinder peut-être que c'est utile. Malheureusement, c'est un outil "fait maison" avec les problèmes suivants,
- développé en Vb.Net
- le code Source doit être compilé
- très lent et consomme de la mémoire.
- aucune aide disponible.
GCC a un indicateur (- save-temps) avec lequel vous pouvez enregistrer des fichiers intermédiaires. Cela comprend .II fichiers, qui sont les résultats du préprocesseur (donc avant la compilation). Vous pouvez écrire un script pour analyser cela et déterminer le poids / coût / Taille de ce qui est inclus, ainsi que l'arbre de dépendance.
J'ai écrit un script Python pour faire cela (disponible publiquement ici: https://gitlab.com/p_b_omta/gcc-include-analyzer).