Как ускорить g++ время компиляции (при использовании большого количества шаблонов)

Этот вопрос, возможно, как-то странный, но как я могу ускорить время компиляции g++? Мой код на С++ сильно использует boost и templates. Я уже перемещался как можно больше из файлов заголовков и использовал параметр -j, но для компиляции (и ссылки) требуется довольно много времени.

Есть ли какие-нибудь инструменты, которые анализируют мой код и указывают на горлышки для компилятора? Или можно каким-то образом профилировать компилятор на моем коде? Это было бы очень приятно, потому что иногда у меня создается впечатление, что я слишком долго смотрел на журнал консоли компилятора...

Ответ 1

Что для меня было самым полезным:

  • Построить на файловой системе RAM. Это тривиально для Linux. Возможно, вы захотите также сохранить копию общих файлов заголовков (предварительно скомпилированных или фактических файлов .h) в файловой системе RAM.
  • Предварительно скомпилированные заголовки. У меня есть одна библиотека (большая) (например, Boost, Qt, stdlib).
  • Объявлять вместо включения классов, где это возможно. Это уменьшает зависимости, таким образом уменьшает количество файлов, которые необходимо перекомпилировать при изменении файла заголовка.
  • Parallelize make. Это обычно помогает в каждом конкретном случае, но у меня есть -j3 глобально для make. Убедитесь, что ваши графики зависимостей верны в вашем файле Makefile, хотя у вас могут быть проблемы.
  • Используйте -O0, если вы не проверяете скорость выполнения или размер кода (и ваш компьютер достаточно быстр, чтобы вы не очень заботились о (возможно, маленьком) достижении производительности).
  • Скомпилируйте каждый раз при сохранении. Некоторым людям это не нравится, но это позволяет вам видеть ошибки на раннем этапе и может выполняться в фоновом режиме, сокращая время ожидания, когда вы закончите писать и готовы к тестированию.

Ответ 2

Вот что я сделал, чтобы ускорить сборку в очень похожем сценарии, который вы описываете (boost, templates, gcc)

  • постройте на локальном диске вместо сетевой файловой системы, такой как NFS
  • обновление до более новой версии gcc
  • исследовать distcc
  • быстрее строить системы, особенно больше оперативной памяти

Ответ 3

Я предполагаю, что мы говорим о минутах, чтобы скомпилировать файл, т.е. предварительно скомпилированные заголовки или проблемы с локальным диском не являются проблемой.

Длительное время компиляции с глубоким кодом шаблона (boost и т.д.) часто связано с недружественным асимптотическим поведением gcc, когда дело доходит до создания экземпляра шаблона, в частности, когда вариативные шаблоны эмулируются с аргументами шаблона по умолчанию.

Здесь документ, который называет сокращенное время компиляции в качестве мотивации для вариативных шаблонов:

В cpptruths была статья о том, как gcc-4.5 намного лучше в этом плане и как он блестяще справляется с его вариативными шаблонами:

IIRC, тогда у BOOST есть способ ограничить генерацию параметров шаблона по умолчанию для псевдодиапазонов, я думаю, что "g++ -DBOOST_MPL_LIMIT_LIST_SIZE = 10" должен работать (по умолчанию 20)

ОБНОВЛЕНИЕ: Существует также хороший поток с общими методами для ускорения компиляции здесь на SO, который может быть полезен:

ОБНОВЛЕНИЕ:. Это касается проблем с производительностью при компиляции шаблонов, принятый ответ рекомендует gcc-4.5, также упоминается как clang как положительный пример:

Ответ 4

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

Дополнительная идея: если ваш код компилируется с clang, используйте его вместо этого. Это обычно быстрее, чем gcc.

Ответ 5

В дополнение к тому, что добавили все остальные и что вы уже делаете (параллельная сборка, параметры компилятора и т.д.), рассмотрите возможность скрытия шаблонов в классах реализации, доступных через интерфейсы. Это означает, что вместо того, чтобы иметь класс:

// ClsWithNoTemplates.h file, included everywhere

class ClsWithTemplates
{
    ComplicatedTemplate<abc> member;
    // ...

public:
    void FunctionUsingYourMember();
};

вы должны иметь:

// ClsWithNoTemplates.h file:

class ClsWithTemplatesImplementation; // forward declaration
  // definition included in the ClsWithNoTemplates.cpp file
  // this class will have a ComplicatedTemplate<abc> member, but it is only 
  // included in your ClsWithNoTemplates definition file (that is only included once)


class ClsWithNoTemplates
{
     ClsWithTemplatesImplementation * impl; // no templates mentioned anywhere here
public:
    void FunctionUsingYourMember(); // call impl->FunctionUsingYourMember() internally
};

Это немного изменит ваш дизайн ООП, но он для хорошего: включая определение "ClsWithNoTemplates" теперь быстро, и вы только (предварительно) скомпилируете определение "ClsWithNoTemplates" один раз.

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

Это изменение должно значительно увеличить время частичной компиляции, а также поможет в том случае, если ваш ClsWithNoTemplates является открытым интерфейсом, экспортированным из файла библиотеки: поскольку файл не изменяется при изменении только реализации, ваш зависимый клиент код не нужно перекомпилировать вообще.

Ответ 7

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

Если есть много файлов, это может значительно сократить время компиляции.

Ответ 8

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

Однако, это простой факт, что С++ - это невероятно сложный язык, и компиляция занимает довольно много времени.

Ответ 9

В этой статье описывается метод компиляции шаблона кода, подобный "традиционным" объектам без шаблонов. Сохраняет время компиляции и связывания с одной строкой служебных данных кода для создания экземпляра шаблона.

Ответ 10

Обычно наиболее дорогими частями компиляции являются (а) чтение исходных файлов (ВСЕ их) и (б) загрузка компилятора в память для каждого исходного файла.

Если у вас есть 52 файла источника (.cc), каждый из которых # включает 47 #include (.h) файлов, вы собираетесь загрузить компилятор 52 раза, и вы собираетесь вспахивать через 2496 файлов. В зависимости от плотности комментариев в файлах, вы можете потратить справедливый кусок времени на употребление бесполезных персонажей. (В одной организации, которую я видел, файлы заголовков варьировались от 66% до 90% комментариев, и только 10% -33% файла были "значимыми". Единственное, что можно было сделать для повышения удобочитаемости этих файлов, было полосой из каждого последнего комментария, оставляя только код.)

Посмотрите, как ваша программа физически организована. Посмотрите, можете ли вы комбинировать исходные файлы и упростить иерархию файлов #include.

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