Поделиться через


Сравнение блоков заголовков, модулей и предварительно скомпилированных заголовков

Исторически вы включали бы стандартную библиотеку с директивой, такой как #include <vector>. Тем не менее, включение файлов заголовков является дорогостоящим, поскольку они повторно обрабатываются в каждом исходном файле, который их содержит.

Предварительно скомпилированные заголовки (PCH) были представлены для ускорения компиляции путем их преобразования один раз и повторного выполнения результата. Но предварительно скомпилированные заголовки могут быть трудно поддерживать.

В C++20 модули появились в качестве значительного улучшения файлов заголовков и предварительно скомпилированных заголовков.

Модули заголовков были введены в C++20 как средство временно перекрыть разницу между файлами заголовков и модулями. Они предоставляют некоторые преимущества в скорости и надёжности модулей, когда вы переносите свой код на использование модулей.

Затем стандартная библиотека C++23 представила поддержку импорта стандартной библиотеки в виде именованных модулей. Это самый быстрый и надежный способ использования стандартной библиотеки.

Чтобы разобраться в различных параметрах, в этой статье сравнивается традиционный #include метод с предварительно скомпилированными заголовками, единицами заголовков и импортом именованных модулей.

Следующая таблица рассортирована по скорости обработки компилятора и надежности, где #include является самым медленным и наименее надежным, а import самым быстрым и наиболее надежным.

Способ Итоги
#include Одним из недостатков является то, что они предоставляют макросы и внутреннюю реализацию. Внутренняя реализация часто представлена как функции и типы, начинающиеся с подчеркивания. Это соглашение, указывающее, что что-то является частью внутренней реализации и не должно использоваться.

Файлы заголовков являются хрупкими, так как порядок #includes может изменять поведение или приводить к сбоям кода и влиять на определения макросов.

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

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

PCH-файлы быстрее, чем #include, но медленнее import.
Единицы заголовков Это новая функция в C++20, которая позволяет импортировать 'корректные' заголовочные файлы в качестве модулей.

Единицы заголовков быстрее, чем #include, проще поддерживать, они значительно компактнее и быстрее, чем предварительно скомпилированные файлы заголовков (PCH).

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

Блоки заголовков медленнее импорта именованного модуля.

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

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

Приближённо оценивая размер файла: 250-мегабайтный PCH-файл может быть представлен 80-мегабайтным файлом единицы заголовков.
Модули Это самый быстрый и надежный способ импорта функциональных возможностей.

Поддержка импорта модулей появилась в C++20. Стандартная библиотека C++23 представляет два именованных модуля, описанных в этом разделе.

При импорте stdвы получаете такие стандартные имена, как std::vector, std::coutно нет расширений, без внутренних вспомогательных элементов, таких как _Sort_unchecked, и без макросов.

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

В качестве грубого приближения размера файла, 250-мегабайтовый PCH-файл может быть представлен 80-мегабайтным файлом заголовка, который может быть представлен 25-мегабайтным модулем.

Именованные модули быстрее, так как при компиляции именованного модуля в .ifc файл и .obj файл компилятор выдает структурированное представление исходного кода, которое можно быстро загрузить при импорте модуля. Компилятор может выполнять некоторые действия, такие как разрешение имен, еще до создания .ifc файла, поскольку именованные модули независимы от порядка и макросов, — значит, эти действия необязательно выполнять во время импорта модуля. В отличие от этого, когда файл заголовка используется, #include его содержимое должно быть предварительно обработано и скомпилировано снова и снова в каждом модуле перевода.

Предварительно скомпилированные заголовки, которые являются моментальными снимками памяти компилятора, могут снизить эти затраты, но не так же, как и именованные модули.

Если в приложении можно использовать функции C++20 и стандартную библиотеку C++23, используйте именованные модули.

Если вы можете использовать функции C++20, но хотите постепенно переходить на модули, используйте единицы заголовков в качестве временного решения.

Если вы не можете использовать функции C++20, используйте #include и рассмотрите предварительно скомпилированные заголовки.

См. также

Файлы предкомпилированных заголовков
Обзор модулей в C++
Руководство. Импорт стандартной библиотеки C++ с помощью модулей
Пошаговое руководство. Импорт библиотек STL в качестве блоков заголовков
Пошаговое руководство. Создание и импорт единиц заголовков в проектах Microsoft C++