Як прискорити компіляцію за допомогою предкомпілірованних
заголовків в С + + Builder h2>
Серебров Борис p>
Precompiled headers
can dramatically increase compilation speeds ... p>
С + + Builder Language Guide p>
Замість вступу відразу наведу приклад. Повна збірка
(build) проекту, що містить близько 170 cpp-модулів, при використанні
предкомпілірованних заголовків відбувається за 811 секунд, при цьому число
оброблених компілятором строк становить 1,808,780. При компіляції того ж
проекту без використання предкомпілірованних заголовків, час збирання
складає 2399 секунд, а кількість рядків, оброблених компілятором - 45,261,820.
Вражає, чи не так? Плата за це прискорення, в принципі не велика --
предкомпілірованний образ, розмір якого близько 40 Мб. p>
При компіляції вихідних текстів, компілятор повинен
обробити всі *. cpp файли проекту і всі включені в них *. h - файли. При цьому
обробляються як налаштовані заголовки, так і стандартні,
такі як vcl.h або Word2k.h. Кількість коду, що знаходиться в стандартних
заголовках може бути дуже великим, наприклад розмір файлу Word2k.h перевищує 5
Мб, у ньому більше 130 000 рядків коду. p>
Так як вміст стандартних заголовків НЕ
змінюється, то їх компіляція при кожній складання проекту є марною
тратою часу. Предкомпілірованние заголовки допомагають вирішити цю проблему --
стандартні файли компілюються один раз, а потім використовується скомпільований
двійковий образ. p>
Принцип дії предкомпілірованних заголовків h2>
Для управління предкомпілірованнимі призначена
директива компілятора # pragma hdrstop. Всі заголовки, включені до
цієї директиви, поміщаються в один образ, наприклад: p>
# include
p>
# include
p>
# pragma hdrstop p>
Така послідовність створить образ, що містить
скомпільовані vcl.h і string. Цей образ буде використаний для іншого
cpp-файлу, якщо в ньому до директиви hdrstop будуть включені ті ж файли, в тому ж
порядку. Зверну увагу, що важливий не тільки склад, а й порядок проходження
заголовків - навіть якщо наступний cpp-файл включає ті ж заголовки, але спочатку
зазначений string, а потім vcl.h, то для цього cpp-файла буде створено новий образ. p>
Таким чином, для повторного використання
предкомпілірованного заголовка необхідно виконання двох умов: p>
- склад включений файлів до директиви hdrstop повинен
бути тим же p>
- послідовність включення файлів до директиви
hdrstop повинна бути тієї ж p>
Скоротити витрати на компіляцію стандартних заголовків
до мінімуму можна тільки в тому випадку, якщо скомпілювати один образ,
що містить всі стандартні заголовки, необхідні для проекту. Для цього потрібно,
щоб: p>
- ВСЕ cpp-файли проекту мали однаковий блок
включений до директиви hdrstop p>
- до цього блоку мають входити ВСЕ стандартні
заголовки, необхідні для проекту p>
Виконати ці умови досить просто, для цього в
початок кожного cpp-файлу необхідно помістити наступні рядки: p>
# include p>
# pragma hdrstop p>
де pch.h - файл, що містить включення всіх
стандартних заголовків: p>
# ifndef PCH_H p>
# define PCH_H p>
# define
INC_VCLDB_HEADERS p>
# define
INC_VCLEXT_HEADERS p>
# include
p>
# include
p>
# include
p>
# include
p>
# include p>
... p>
# endif p>
Повний текст моєї версії цього файлу наведений у кінці
статті. На h-файли, що входять до предкомпілірованний образ, накладається
обмеження - у них не повинно бути ініціалізований даних, наприклад, у
math.hpp є рядки: p>
static const
Extended NaN = 0.0/0.0; p>
static const
Extended Infinity = 1.0/0.0; p>
Через наявність цих констант включити math.hpp в файл
pch.h не можна. p>
До речі, С + + Builder при додаванні нових модулів в
проект реалізує описану стратегію управління предкомпілірованнимі
заголовками. Наприклад, при створенні нової програми, файл Unit1.cpp буде
таким: p>
# include
p>
# pragma
hdrstop p>
# include
"Unit1.h" p>
.... p>
Якщо подивитися на текст vcl.h, то можна побачити, що
він є оболонкою для включення великої кількості інших стандартних
заголовків файлів. p>
Керувати складом що включаються в vcl.h заголовків можна
за допомогою спеціальних символів (INC_VCLDB_HEADERS, INC_VCLEXT_HEADERS та ін.) У
моїй версії pch.h ці символи визначаються за допомогою # define до включення
vcl.h, що призводить до збільшення числа включаються файлів. p>
Як в існуючому проекті перейти до використання
предкомпілірованних заголовків p>
Навіть у великому проекті перейти до використання
предкомпілірованних заголовків досить просто. p>
У властивостях проекту потрібно включити кешування
предкомпілірованних заголовків і рекомендується вказати "персональний"
файл, в якому буде зберігатися образ предкомпілірованних заголовків: Project --
Options - закладка Compiler, група "Pre-compiled headers". Тут
повинно бути вибрано "Cache pre-compiled headers", а в полі "File
Name "потрібно ввести" pch.csm ". При такій настройці образ з
предкомпілірованнимі заголовками буде знаходиться в папці з проектом, у файлі pch.csm.
p>
Після цього на початку кожного cpp-модуля необхідно
вставити 2 рядки: p>
# include "pch.h" p>
# pragma hdrstop p>
Всі раніше включені заголовки залишаються на
своїх місцях, їх видаляти не треба. Наприклад: p>
# include "pch.h"// включає vcl.h, string
і т.д. p>
# pragma hdrstop p>
# include p>
# include p>
... p>
Так як у всіх стандартних заголовках застосовуються
варти повторного включення, то повторний їх згадка не тягне за собою
повторного включення. p>
У принципі, при використанні pch.h, технічна
потреба у включенні стандартних заголовків зникає. Однак, корисно все
ж вказувати всі необхідні для кожного конкретного модуля заголовки нижче
директиви # pragma hdrstop. По-перше, це певною мірою документує
модуль - по включаються файлів можна судити, якими можливостями користується
цей модуль. По-друге, це полегшує повторне використання модуля в інших
проектах, в яких або не використовується pch.h, або його вміст може бути
іншим. p>
Теоретично можна ще більше підвищити ефективність
компіляції, якщо включити в pch.h не тільки стандартні, але й усі
призначені для користувача заголовки. Практично, так як налаштовані
заголовки міняються досить часто, це може спричинити за собою часту
перекомпіляції pch.h, що негативно позначиться на часу компіляції. Крім того,
призначені для користувача заголовки зазвичай не бувають дуже великими і компілюються
дуже швидко. Тому включати їх pch.h не доцільно. p>
Як перевірити, що предкомпілірованние заголовки
використовуються ефективно h2>
При додаванні в проект нових файлів потрібно не забувати
включати до них pch.h, інакше для них не буде використаний загальний
предкомпілірованний образ. Така ж ситуація може виникнути, якщо в якомусь
модулі включаються стандартні заголовки, які не увійшли до pch.h. Для того,
щоб відстежити такі файли, є кілька способів: p>
- візуальне спостереження за процесом компіляції.
Зазвичай, кількість стрічок компільованих в одному файлі не повинно перевищувати 10000-15000
строк p>
- якщо для проекту вибрано індивідуальний файл для
зберігання образу предкомпілірованних заголовків і цей файл називається pch.csm,
то потрібно звернути увагу на наявність допоміжних файлів з іменами pch. # 00,
pch. # 01 і т.д. Якщо для всіх файлів проекту використовується один і той же
предкомпілірованний образ, то допоміжний файл повинен бути тільки одна --
pch. # 00. Якщо таких файлів більше, це означає що для якихось cpp-модулів
створюються додаткові образи. p>
Текст pch.h p>
# ifndef PCH_H p>
# define PCH_H p>
# define
INC_VCLDB_HEADERS p>
# define
INC_VCLEXT_HEADERS p>
# include
p>
/* Все, що підключається попередніми 3-ма рядками p>
// Core (minimal)
Delphi RTL headers p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
// Core (minimal)
VCL headers p>
# if defined (INC_VCL) p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
// VCL Database
related headers p>
# if
defined (INC_VCLDB_HEADERS) p>
# include
p>
# include
p>
# include
p>
# endif//
INC_VCLDB_HEADERS p>
// Full set of VCL
headers p>
# if
defined (INC_VCLEXT_HEADERS) p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# endif// INC_VCLEXT_HEADERS p>
# endif//
INC_VCL p>
*/ p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include
p>
# include