Как измерить кандидатов на прыщи?

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

У меня есть опыт использования pimpl, сокращающего время компиляции проекта с двух часов до десяти минут, но я сделал это, просто следуя своим инстинктам: я рассудил, что файлы заголовков классов, которые включают (1) много исходного кода, (2) сложны /template, являются лучшими кандидатами для использования идиомы pimple.

Есть ли инструмент, который объективно указывает, какие классы являются хорошими кандидатами на прыщи?


person richelbilderbeek    schedule 27.02.2016    source источник
comment
ОТ: У меня всегда сложилось впечатление, что основная причина использования PIMPL заключалась в том, чтобы не раздувать общедоступный интерфейс деталями реализации. В любом случае, отличный вопрос   -  person sjaustirni    schedule 27.02.2016
comment
Вы пытались использовать предварительно скомпилированные заголовки, чтобы увидеть, каков прирост производительности во время компиляции?   -  person Christophe    schedule 27.02.2016
comment
@Christophe: да, я использовал предварительно скомпилированные заголовки. В ответ на мой первоначальный вопрос будет: «Как измерить предварительно скомпилированные заголовки-кандидаты?»   -  person richelbilderbeek    schedule 28.02.2016
comment
Я считаю, что это очень зависит от компилятора. Замедление вызвано двумя основными причинами: размером заголовка (дисковый ввод-вывод, потому что даже с gards нужно читать полный заголовок) и сложностью объекта (количество функций, классов, членов), определенных (лексический и синтаксический анализ и т. д.). Я думаю, что вы могли бы использовать такие основные показатели, чтобы сделать свой выбор. Я предполагаю, что будущие модули С++ могли бы помочь улучшить это с pimpl или без него.   -  person Christophe    schedule 28.02.2016
comment
Это для gcc или против?   -  person VladimirS    schedule 04.03.2016
comment
для gcc я бы предложил следующее: используйте strace (проверьте время, затрачиваемое на открытие/закрытие), параметр -frepo (для создания файлов шаблонов .rpo) и -ftime-report. Это даст вам некоторые идеи о том, где провел время. Вы можете использовать прагму один раз поверх включения защиты.   -  person VladimirS    schedule 04.03.2016
comment
@VladimirS: я использую gcc. Я попробую strace и отчитаюсь позже   -  person richelbilderbeek    schedule 06.04.2016


Ответы (2)


Это правда, что Pimpl полезен для инкрементной компиляции.

Но основная причина использования Pimpl — сохранить совместимость с ABI. Это было правилом в моей прошлой компании почти для всех публичных классов в API.

Другое преимущество заключается в том, что вы также можете распространять свою библиотеку в виде пакета, содержащего заголовок, который не раскрывает детали реализации.

Для этого я скажу: используйте Pimpl везде, где это возможно.

Очень хорошая статья о деталях реализации Qt Pimpl и преимуществах: https://wiki.qt.io/D-Pointer

Проблема времени компиляции должна быть решена с помощью:

  • используя предварительно скомпилированный заголовок
  • разделение ваших больших проектов на маленькие по частоте касания кода. Части, которые часто не изменяются, могут быть скомпилированы в библиотеке и опубликованы в локальном репозитории, на который другие проекты ссылаются по версии.
  • ...
person saad    schedule 18.08.2016
comment
Спасибо, Саад, за то, что просветил меня по этим вопросам. Тем не менее, мой вопрос в том, как измерить, где использование pimpl даст наибольшее увеличение времени компиляции. Ближе всего к этому ответу ваше предложение «разделить ваши большие проекты на маленькие по частоте касания кода». Мой вопрос: «Как вы это измеряете?» - person richelbilderbeek; 22.08.2016

Я не знаю о существующем инструменте для этого, но я бы предложил:

Во-первых, измерьте стоимость отдельного включения каждого заголовка. Составьте список всех заголовков и предварительно обработайте каждый заголовок. Простейшей мерой стоимости этого заголовка является количество строк, полученных в результате предварительной обработки. Возможно, более точной мерой было бы подсчет вхождений «шаблона», поскольку, по моему опыту, обработка определений шаблона, по моему опыту, доминирует над временем компиляции. Вы также можете подсчитать количество вхождений 'inline', поскольку я видел, что большое количество встроенных функций, определенных в заголовках, также является проблемой (но имейте в виду, что встроенные определения методов класса не обязательно используют ключевое слово).

Затем измерьте количество единиц перевода (ЕП), содержащих этот заголовок. Для каждого основного файла TU (например, файла .cpp) предварительно обработайте этот файл и соберите набор отдельных заголовков, которые появляются в выходных данных (в строках #). После этого инвертируйте это, чтобы получить карту из заголовка в количество TU, которые его используют.

Наконец, для каждого заголовка умножьте его отдельную стоимость на количество TU, которые его включают. Это мера совокупного влияния этого заголовка на общее время компиляции. Отсортируйте этот список и просмотрите его в порядке убывания, перемещая частные детали реализации в связанный файл реализации и соответствующим образом обрезая публичный заголовок.

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

Один из способов сосредоточить усилия, чтобы это не было "журавлем в небе", состоит в том, чтобы начать с выбора единственной TU, компиляция которой занимает больше всего времени, и работать над оптимизацией только тех заголовков, от которых она зависит. После того, как вы значительно сократите время для этого TU, снова взгляните на общую картину. И если вы не можете значительно улучшить время компиляции этого одного TU с помощью метода частной реализации, то это означает, что вам нужно рассмотреть другие подходы для этой кодовой базы.

person Scott McPeak    schedule 15.03.2020