Включение инструментов статического анализа в файл заголовка?
Недавно коллега показал мне, что один исходный файл содержит более 3400 заголовков во время компиляции. У нас есть более 1000 единиц перевода, которые скомпилируются в сборке, что приводит к огромному штрафу за производительность над заголовками, которые, безусловно, не все используются.
Существуют ли какие-либо инструменты статического анализа, которые могли бы пролить свет на деревья в таком лесу, в частности, дать нам возможность решить, какие из них мы должны использовать для обработки?
UPDATE
Нашел интересную информацию о стоимости включения файла заголовка (и типы включенных охранников для оптимизации его включения) здесь, исходящий от этого вопроса.
Ответы
Ответ 1
Вывод gcc -w -H <file>
может быть полезен (если вы его разобрали и поместили некоторые подсчеты), -w
будет подавлять все предупреждения, которые могут быть неудобны для работы.
В документах gcc:
-H
Распечатайте имя каждого используемого файла заголовка в дополнение к другим обычным действиям. Каждое имя имеет отступы, чтобы показать, насколько глубоко в #include
stack это есть. Предварительно скомпилированные файлы заголовков также печатаются, даже если они признаны недействительными; недопустимый предварительно скомпилированный заголовок файл печатается с ...x
и действительным с ...!
.
Результат выглядит следующим образом:
. /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
Ответ 2
Если вы используете gcc/g++, параметр -M
или -MM
будет выводить строку с запрашиваемой вами информацией. (Первый будет включать в себя системные заголовки, а второй - нет. Существуют и другие варианты, см. Руководство.)
$ 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
Вам нужно будет удалить foo.o: foo.c
в начале, но остальное - список всех заголовков, от которых зависит файл, поэтому было бы нелегко написать script для их сбора и суммирования.
Конечно, это предложение полезно только для Unix, и только если у кого-то нет лучшей идеи.: -)
Ответ 3
несколько вещей -
-
используйте только "предварительный процесс", чтобы посмотреть на ваш препроцессорный вывод. gcc -E, другие компиляторы также имеют функцию
-
использовать предварительно скомпилированные заголовки.
-
gcc имеет параметры -verbose и -trace, которые также отображают полное дерево include, MSVC имеет параметр /showIncludes, найденный в странице свойств Advanced С++
Кроме того, Отображение иерархии #include для файла С++ в Visual Studio
Ответ 4
GCC имеет флаг -M
, который выведет список зависимостей для данного исходного файла. Вы можете использовать эту информацию, чтобы выяснить, какие из ваших файлов имеют большинство зависимостей, от которых больше всего зависят файлы и т.д.
Подробнее о странице руководства. Существует несколько вариантов -M
.
Ответ 5
"Конструкция программного обеспечения большого масштаба С++" Джона Лакоса имели инструменты, которые извлекали зависимости времени компиляции между исходными файлами.
К сожалению, их репозиторий на сайте Addison-Wesley отсутствует (вместе с самим сайтом AW), но я нашел здесь tarball:
http://prdownloads.sourceforge.net/introspector/LSC-rpkg-0.1.tgz?download
Я нашел полезным несколько рабочих дней назад, и он имеет силу быть свободным.
Кстати, если вы не читали книгу Лакоса, похоже, что ваш проект принесет пользу. (Текущая версия немного устарела, но я слышал, что у Lakos есть еще одна книга, выходящая в 2012 году.)
Ответ 6
Лично я не знаю, есть ли инструмент, который скажет "Удалить этот файл". Это действительно сложный вопрос, который зависит от многих вещей. Глядя на дерево заявлений с включением, наверняка будет сводить вас с ума... Это сбило бы меня с ума, а также разорило бы мои глаза. Есть лучшие способы сделать что-то, чтобы сократить время компиляции.
- Разверните методы класса.
- После того, как они деинсталлируются, переустановите свои заявления include и попытайтесь удалить их. Обычно полезно удалять их и начинать.
- Предпочитают использовать форвардные объявления, насколько это возможно. Если вы де-встроите методы в свои файлы заголовков, вы можете сделать это много.
- Разбить большие файлы заголовков на более мелкие файлы. Если класс в файле используется чаще, чем большинство, то сам поместить его в файл заголовка.
- 1000 трансляционных единиц на самом деле не очень. У нас от 10 до 20 тысяч.:)
- Получить Incredibuild, если время компиляции слишком длинное.
Ответ 7
Я слышал, что есть некоторые инструменты, но я их не использую.
Я создал какой-то инструмент https://sourceforge.net/p/headerfinder, возможно, это полезно. К сожалению, это инструмент "HOME MADE" со следующими проблемами:
- Разработано в Vb.Net
- Исходный код необходимо скомпилировать
- Очень медленно и потребляет память.
- Нет помощи.
Ответ 8
GCC Имеет флаг (-save-temps), с помощью которого вы можете сохранять промежуточные файлы. Это включает файлы .ii, которые являются результатами препроцессора (поэтому перед компиляцией). Вы можете написать script, чтобы проанализировать это и определить вес/стоимость/размер того, что включено, а также дерево зависимостей.
Я написал Python script, чтобы сделать это (общедоступно здесь: https://gitlab.com/p_b_omta/gcc-include-analyzer).