Является ли сокращение числа единиц перевода cpp хорошей идеей?

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

Итак, я придумал следующий трюк:

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

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

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

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

Ответы

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

    с. Конфликты имен более вероятны

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

Ответ 6

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

Ответ 7

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

Ответ 8

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