Предварительно откомпилированные заголовки

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

Предварительно откомпилированные заголовки (англ. pre-compiled headers) — в средах программирования на языках Си и Си++ — способ ускорить компиляцию программ за счёт предварительной обработки так называемых заголовочных файлов, которые содержат интерфейсы модулей и, согласно нормам данных языков программирования, подключаются к программе путём прямой вставки их текстов в тело основной программы с помощью специальной директивы препроцессора #include. Предкомпилированные заголовки сохраняются на диске в виде файлов во внутреннем формате компилятора и при повторных компиляциях проекта время на их обработку и подключение существенно сокращается.

Предпосылки

[править | править код]

Язык программирования Си для подключения внешнего модуля использовал заголовочные файлы. Это упрощает компилятор, и даёт некоторую гибкость — и это работало в течение 20 лет, пока заголовки были небольшими, а файлов в проекте — мало. С укрупнением программных проектов время компиляции стало расти квадратично[1] — увеличивается как количество единиц компиляции, так и количество заголовков, подключённых к каждой конкретной единице. В особо крупных проектах полная перекомпиляция занимает десятки минут и чаще выполняется во время ночной сборки, чем на рабочих местах программистов.

К тому же язык программирования Си относится к семейству LR(1), и поэтому компиляторы Си медленнее, например, Паскаля (который LL(1))[источник не указан 4177 дней].

Чтобы сократить время компиляции, и были разработаны предкомпилированные заголовки. Поскольку заголовочные файлы меняются гораздо реже файлов, содержащих код программы (а библиотечные — практически никогда), разумным средством оптимизации оказалось выполнять предварительную обработку заголовков, и преобразование их в файлы специального вида, которые при компиляции программы можно подключать, минуя первые стадии компиляции. За счёт прекомпиляции заголовков полной обработке компилятором подвергаются только изменённые части программы. Впрочем, предварительная компиляция заголовка помогает не всегда:

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

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

Реализации

[править | править код]

Предкомпилируется всё, что идёт до директивы #pragma hdrstop.

// main.cpp

///// Предкомпилируемые заголовки

// C++
# include <cstdio>

// STL
# include <string>
# include <vector>
# include <map>
# include <algorithm>

// Специфичные для ОС
# include <windows.h>

///// Тут предкомпиляция заканчивается
# pragma hdrstop

///// Остальные заголовки
# include "unit1.h"

int main()
{
  doUnit1();
  return 0;
}

При компиляции h-файлов GCC автоматически предполагает, что требуется делать предкомпилированный заголовок (либо это можно указать через командную строку, ключ -x). Большинство оболочек программирования (например, Code::Blocks) позволяют указывать, какие файлы предкомпилировать. Стандартным решением будет «свалить» наиболее используемые, но редко меняющиеся заголовки в один h-файл и подключать его самым первым.

// pch.h - его предкомпилировать (задаётся в makefile или проекте).

// C++
# include <cstdio>
# include <cmath>

// STL
# include <string>
# include <vector>
# include <map>
# include <algorithm>

// Специфичные для ОС
# include <windows.h>
// unit1.cpp
  // предкомпилируемый - первым!
# include "pch.h"
  // остальные заголовки
# include "unit1.h"

void doUnit1()
{
}
// main.cpp
  // предкомпилируемый - первым!
# include "pch.h"
  // остальные заголовки
# include "unit1.h"

int main()
{
  doUnit1();
  return 0;
}

Ситуация похожа на GCC. Visual C++ версии 6.0 и выше предкомпилирует заголовок со стандартным именем — stdafx.h (это имя можно изменить опцией компилятора).

В IDE мастер создания проектов позволяет выбрать, следует ли использовать предкомпилированные заголовки. При их использовании будет автоматически добавлена опция компилятора /Yu'stdafx.h', а компиляция любого файла, не содержащего #include "stdafx.h", будет остановлена с ошибкой.

Примечания

[править | править код]
  1. Large-scale C++ Software Design Архивная копия от 14 декабря 2014 на Wayback Machine John Lakos, 1996, p 334 " ... reduce the cost of compiling by eliminating the quadratic behavior we observed in the time spent by the C++ preprocessor."