Подтвердить что ты не робот

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

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

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

UPDATE

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

4b9b3361

Ответ 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).