2009-05-14 2 views
13

Я нахожу, что если есть много классов, время компиляции резко увеличивается, когда я использую один * .h и один * .cpp-файл для каждого класса. Я уже использую скомпилированные заголовки и инкрементное связывание, но все время компиляции очень длинное (да я использую импульс;)Уменьшает количество единиц перевода cpp - хорошая идея?

Так что я придумал следующую хитрость:

  • определен * .cpp файлов как не -compilable
  • определены * .cxx файлы в компилируемый
  • добавил один * .cxx файл на приложение модуля и #included все * .cpp файлы этого модуля в нем.

Таким образом, вместо 100 единиц перевода я получил всего 8 единиц перевода. Время компиляции сократилось в 4-5 раз.

Недостатки в том, что вы должны вручную включать все файлы * .cpp (но это не кошмар для обслуживания, так как если вы забудете включить что-то, на которое ссылается компоновщик) и что некоторые удобства VS IDE не работают с этой схемой, например Go To/Move to Implementation и т. Д.

Итак, вопрос в том, имеет ли множество единиц перевода cpp действительно единственный истинный способ? Является ли мой трюк известным шаблоном, или, может быть, я что-то упускаю? Спасибо!

ответ

4

Я видел, что вы делаете в видеоиграх, так как он помогает компилятору делать оптимизации, иначе он не мог бы сделать, а также сэкономить много памяти. Я видел, что «сборщик uber» и «массовая сборка» относятся к этой идее. И если это помогает ускорить вашу сборку, почему бы и нет.

5

Одним из существенных недостатков этого подхода является наличие одного .obj-файла для каждой единицы перевода.

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

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

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

+0

Visual Studio поддерживает связь на уровне функций. Тем не менее, это актуально для Unix/Linux. – MSalters

+0

Будет ли связывание на уровне функций исключать только функции или переменные? – sharptooth

1

Больше и меньше единиц перевода не используют параллельную компиляцию. Я не знаю, какие компиляторы и какие платформы вы используете, но компиляция в параллельном множестве единиц перевода может значительно уменьшить время сборки ...

+0

Это не обязательно так - OP указывает, что он использует Visual Studio, а последние версии могут использовать несколько процессоров для компиляции одного исходного файла, если вы добавите соответствующий параметр командной строки. –

+0

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

3

Объединение большего количества файлов исходного кода на C++ в один файл - это подход о чем уже упоминалось несколько раз в последнее время, особенно когда люди строят большие системы и занимаются сложными файлами заголовков (тогда это будет повышаться).

Как вы упоминаете VS, я обнаружил, что количество включенных файлов в проекте и особенно размер пути включения, по-видимому, влияет на время компиляции Visual C++ гораздо больше, чем на время компиляции g ++.Это особенно относится к множеству вложенных включений (опять-таки, делает это), так как требуется большое количество поисков файлов для поиска всех включенных файлов, требуемых исходным кодом. Объединение кода в один исходный файл означает, что компилятор может быть намного умнее в поиске упомянутых включений, плюс их, очевидно, будет меньше, так как вы ожидаете, что файлы в одном и том же подпроекте будут, вероятно, включать очень похожие набор файлов заголовков.

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

0

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

2

Я не уверен, что это актуально в вашем случае, но, возможно, вы можете использовать объявление вместо определения, чтобы уменьшить количество #include, которое вам нужно сделать. Кроме того, возможно, вы можете использовать идиому pimpl для той же цели. Это, мы надеемся, уменьшит количество исходных файлов, которые необходимо перекомпилировать каждый раз, и количество заголовков, которые нужно втянуть.

3

Я не думаю, что сокращение количества единиц компиляции - хорошая идея. Вы пытаетесь решить проблему с большим временем компиляции, и этот подход, похоже, помогает в этом, но то, что вы получаете дополнительно:

  1. Увеличенное время компиляции при разработке. Обычно разработчик модифицирует несколько файлов за раз, а компиляция, вероятно, будет быстрее для 3-4 небольших файлов, а затем для одного очень большого файла.
  2. Как вы упомянули, сложнее перемещаться по коду, ИМХО это чрезвычайно важно.
  3. Вы можете иметь некоторые помехи между .cpp файлов, включенных в один файл .cxx:

    а. Общепринятой практикой является определение локально в файле cpp file (for debug builds) new new для проверки утечки памяти. К сожалению, это невозможно сделать, прежде чем включать заголовки с использованием нового места размещения (как некоторые заголовки STL и BOOST)

    b. Обычно использовать добавления объявлений в файлах cpp. При вашем подходе это может привести к проблемам с заголовками, включенными позже

    c. Имя конфликты более вероятны

ИМХО, гораздо чище (но, возможно, более дорогой способ), чтобы ускорить время компиляции, чтобы использовать некоторые распределенные системы сборки. Они особенно эффективны для чистых сборок.