Инструменты статического анализа включения заголовочного файла?

Недавно мой коллега сообщил мне, что один наш исходный файл содержит более 3400 заголовков во время компиляции. У нас более 1000 модулей перевода, которые компилируются в сборке, что приводит к огромному снижению производительности по сравнению с заголовками, которые, конечно же, используются не все.

Существуют ли какие-либо инструменты статического анализа, которые могли бы пролить свет на деревья в таком лесу, в частности, давая нам возможность решить, какие из них нам следует поработать над обработкой?

ОБНОВИТЬ

Находя здесь некоторую интересную информацию о стоимости включения заголовочного файла (и о типах включаемых охранников для оптимизации его включения) здесь, исходя из этого вопроса.

8 ответов

Решение

Выход из gcc -w -H <file> может быть полезным (если вы проанализируете это и добавите некоторые значения) -w здесь, чтобы подавить все предупреждения, с которыми может быть неудобно иметь дело.

Из документации GCC:

-ЧАС

Напечатайте имя каждого используемого файла заголовка, в дополнение к другим обычным действиям. Каждое имя имеет отступ, чтобы показать, насколько глубоко #include стек это. Предварительно скомпилированные заголовочные файлы также печатаются, даже если они признаны недействительными; неверный предварительно скомпилированный заголовочный файл печатается с ...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

Если вы используете 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 в начале, но остальное - это список всех заголовков, от которых зависит файл, поэтому было бы не сложно написать сценарий для их сбора и суммирования.

Конечно, это предложение полезно только в Unix и только в том случае, если у кого-то нет идеи получше.:-)

Несколько вещей-

  • используйте "только препроцесс", чтобы посмотреть на ваш вывод препроцессора. опция gcc -E, другие компиляторы тоже имеют функцию

  • использовать предварительно скомпилированные заголовки.

  • В gcc есть опции -verbose и --trace, которые также отображают полное дерево включения, MSVC имеет параметр /showInclude, который можно найти на странице свойств Advanced C++.

Кроме того, отображение иерархии #include для файла C++ в Visual Studio

У "Большого масштаба C++ Software Design" Джона Лакоса были инструменты, которые извлекали зависимости времени компиляции среди исходных файлов.

К сожалению, их хранилище на сайте Addison-Wesley исчезло (вместе с самим сайтом AW), но я нашел тарбол здесь: http://prdownloads.sourceforge.net/introspector/LSC-rpkg-0.1.tgz?download

Я нашел это полезным несколько работ назад, и он достоин того, чтобы быть свободным.

Кстати, если вы не читали книгу Лакоса, похоже, ваш проект принесет пользу. (Нынешнее издание немного устарело, но я слышал, что у Лакоса есть еще одна книга, выходящая в 2012 году.)

GCC имеет -M флаг, который выведет список зависимостей для данного исходного файла. Вы можете использовать эту информацию, чтобы выяснить, какие из ваших файлов имеют наибольшую зависимость, от каких файлов больше всего зависит и т. Д.

Проверьте справочную страницу для получения дополнительной информации. Есть несколько вариантов -M,

Лично я не знаю, есть ли инструмент, который скажет "Удалить этот файл". Это действительно сложный вопрос, который зависит от многих вещей. Глядя на дерево включенных утверждений, вы наверняка сведете вас с ума... Это сведет меня с ума и испортит мне глаза. Существуют более эффективные способы уменьшить время компиляции.

  1. Отключите ваши методы класса.
  2. После того, как вы удалите их, повторно изучите включенные операторы и попытайтесь удалить их. Обычно полезно удалить их и начать заново.
  3. Предпочитаю использовать форвардные объявления максимально. Если вы деинлайн-методы в заголовочных файлах, вы можете сделать это много.
  4. Разбейте большие заголовочные файлы на более мелкие. Если класс в файле используется чаще, чем большинство, то поместите его в заголовочный файл сам по себе.
  5. 1000 переводческих единиц это не очень много на самом деле. У нас между 10-20 тысячами.:)
  6. Получите Incredibuild, если время компиляции все еще слишком велико.

Я слышал, что есть некоторые инструменты, но я ими не пользуюсь.

Я создал какой-то инструмент https://sourceforge.net/p/headerfinder может быть, это полезно. К сожалению, это инструмент "HOME MADE" со следующими проблемами,

  • Разработано в Vb.Net
  • Исходный код надо скомпилировать
  • Очень медленно и потребляет память.
  • Помощь не доступна.

GCC Имеет флаг (-save-temps), с помощью которого вы можете сохранять промежуточные файлы. Это включает в себя файлы.ii, которые являются результатами препроцессора (так до компиляции). Вы можете написать скрипт для его анализа и определения веса / стоимости / размера того, что включено, а также дерева зависимостей.

Я написал скрипт на Python для этого (общедоступно здесь: https://gitlab.com/p_b_omta/gcc-include-analyzer).

Другие вопросы по тегам