З builder як прискорити компіляцію з допомогою предкомпілірованних заголовків

С ++ Builder: як прискорити компіляцію з допомогою предкомпілірованних заголовків

С ++ Builder: як прискорити компіляцію з допомогою предкомпілірованних заголовків


Precompiled headers can dramatically increase
compilation speeds.
С ++ Builder Language Guide


Замість вступу відразу наведу приклад. Повна збірка (build) проекту, що містить близько 170 cpp-модулів, при використанні предкомпілірованних заголовків відбувається за 811 секунд, при цьому число оброблених компілятором рядків становить 1,808,780. При компіляції того ж проекту без використання предкомпілірованних заголовків, час складання становить 2399 секунд, а число рядків, оброблених компілятором - 45,261,820. Вражає, чи не так? Плата за це прискорення, в принципі не велика - предкомпілірованний образ, розмір якого близько 40 Мб.

При компіляції вихідних текстів, компілятор повинен обробити всі * .cpp файли проекту і всі включені в них * .h - файли. При цьому обробляються як призначені для користувача заголовки, так і стандартні, такі як vcl.h або Word2k.h. Кількість коду, що знаходиться в стандартних заголовках може бути дуже великим, наприклад розмір файлу Word2k.h перевищує 5 Мб, в ньому понад 130 000 строк коду.

Так як вміст стандартних заголовків не змінюється, то їх компіляція за будь-якої збірці проекту є марною тратою часу. Предкомпілірованние заголовки допомагають вирішити цю проблему - стандартні файли компілюються один раз, а потім використовується скомпільований двійковий образ.

Принцип дії предкомпілірованних заголовків
Для управління предкомпілірованнимі призначена директива компілятора #pragma hdrstop. Всі заголовки, включені до цієї директиви, поміщаються в один образ, наприклад:

Така послідовність створить образ, що містить скомпільовані vcl.h і string. Цей образ буде використаний для іншого cpp-файлу, якщо в ньому до директиви hdrstop будуть включені ті ж файли, в тому ж порядку. Зверну увагу, що важливий не тільки склад, але і порядок проходження заголовків - навіть якщо наступний cpp-файл включає ті ж заголовки, але спочатку зазначений string, а потім vcl.h, то для цього cpp-файлу буде створений новий образ.

Таким чином, для повторного використання предкомпілірованного заголовка необхідним є дотримання двох умов:

    - склад включених до директиви hdrstop файлів повинен бути тим же
    - послідовністьвключення файлів до директиви hdrstop повинна бути тієї ж

Скоротити витрати на компіляцію стандартних заголовків до мінімуму можна тільки в тому випадку, якщо скомпілювати один образ, що містить всі стандартні заголовки, необхідні для проекту. Для цього потрібно, щоб:
    - ВСЕ cpp-файли проекту мали однаковий блок включень до директиви hdrstop
    - в цей блок має входити ВСЕ стандартні заголовки, необхідні для проекту

Виконати ці умови досить просто, для цього в початок кожного cpp-файлу необхідно помістити такі рядки:

де pch.h - файл, що містить включення всіх стандартних заголовків:

Повний текст моєї версії цього файлу наведено в кінці статті. На h-файли, що входять до предкомпілірованний образ, накладається обмеження - в них не повинно бути ініціалізованих даних, наприклад, в math.hpp є рядки:

Через наявність цих констант включити math.hpp в файл pch.h не можна.
До речі, С ++ Builder при додаванні нових модулів в проект реалізує описану стратегію управління предкомпілірованнимі заголовками. Наприклад, при створенні нової програми, файл Unit1.cpp буде таким:

Якщо подивитися на текст vcl.h, то можна побачити, що він є оболонкою для включення великої кількості інших стандартних заголовків файлів.

Управляти складом включаються в vcl.h заголовків можна за допомогою спеціальних символів (INC_VCLDB_HEADERS, INC_VCLEXT_HEADERS і ін.). У моїй версії pch.h ці символи визначаються з допомогою #define до включення vcl.h, що призводить до збільшення числа включаються файлів.

Як в існуючому проекті перейти до використання предкомпілірованних заголовків

Навіть у великому проекті перейти до використання предкомпілірованних заголовків досить просто.

Насамперед потрібно створити сам заголовки pch.h. Його потрібно розмістити в папці з проектом, вміст цього файлу наведено в кінці статті.

Потім у властивостях проекту потрібно включити кешування предкомпілірованних заголовків, рекомендується також вказати "персональний" файл, в якому буде зберігатися образ предкомпілірованних заголовків: Project - Options - закладка Compiler, група "Pre-compiled headers". Тут має бути вибрано "Cache pre-compiled headers", а в полі "File Name" потрібно ввести "pch.csm". При такій настройці образ з предкомпілірованнимі заголовками перебуватиме в папці з проектом, у файлі pch.csm.

Після цього в початок кожного cpp-модуля необхідно вставити 2 рядки:

Всі раніше включені заголовки залишаються на своїх місцях, їх видаляти не треба. наприклад:

Так як у всіх стандартних заголовках застосовуються варти повторного включення, то повторне їх згадка не тягне за собою повторного включення.

В принципі, при використанні pch.h, технічна потреба у включенні стандартних заголовків зникає. Однак, корисно все ж вказувати всі необхідні для кожного конкретного модуля заголовки нижче директиви #pragma hdrstop. По-перше, це в деякій мірі документує модуль - по включених файлів можна судити, якими можливостями користується цей модуль. По-друге, це полегшує повторне використання модуля в інших проектах, в яких або не використовується pch.h, або його вміст може бути іншим.

Теоретично можна ще більше підвищити ефективність компіляції, якщо включити в pch.h не тільки стандартні, але і всі призначені для користувача заголовки. Практично, так як призначені для користувача заголовки змінюються досить часто, це може спричинити за собою часту перекомпіляцію pch.h, що негативно позначиться на часі компіляції. Крім того, призначені для користувача заголовки зазвичай не бувають дуже великими і компілюються дуже швидко. Тому включати їх pch.h не доцільно.

Як перевірити, що предкомпілірованние заголовки використовуються ефективно

При додаванні в проект нових файлів потрібно не забувати включати в них pch.h, інакше для них не буде використаний загальний предкомпілірованний образ. Така ж ситуація може виникнути, якщо в якомусь модулі включаються стандартні заголовки, які не ввійшли в pch.h. Для того, щоб відстежити такі файли, є кілька способів:

    - візуальне спостереження за процесом компіляції. Зазвичай, число рядків компільованих в одному файлі не повинно перевищувати 10000-15000 рядків
    - якщо для проекту обраний індивідуальний файл для зберігання образу предкомпілірованних заголовків і цей файл називається pch.csm, то потрібно звернути увагу на наявність допоміжних файлів з іменами pch. # 00, pch. # 01 і т.д. Якщо для всіх файлів проекту використовується один і той же предкомпілірованний образ, то допоміжний файл повинен бути тільки один - pch. # 00. Якщо таких файлів більше, це означає що для якихось cpp-модулів створюються додаткові образи.
    - у властивостях проекту включити опцію Show General Messages (Project -> Options -> закладка Compiler). Тоді в вікно Messages буде виводиться додаткова інформація в процесі компіляції, в тому числі інформація про завантаження предкомпілірованних заголовків. Якщо для файлу завантажений предкомпілірованний образ, то у вікні Messages після його імені буде виведено "[C ++] Loaded pre-compiled headers".


Про вищевикладеному
Якщо з приводу викладеного матеріалу є питання і зауваження, то я готовий їх прийняти - пишіть [email protected].


P.S.
Мені кілька разів повідомляли про проблеми з предкомпілірованнимі заголовками - що Builder починає видавати попередження "Dialogs.hpp (437): W8058 Can not create pre-compiled header: initialized data in header" і не створює предкомпілірованний заголовок.
Через що це виникає, я так і не зрозумів, але, як написав мені Алеутдінов Сергій Харізовіч, йому вдалося знайти обхідний шлях:
цитата:

Проблему вдалося вирішити "обхідним маневром".

Створив файл заголовків AllHeaders.hpp, куди включив все не VCL хёдери (DevExpress, FIBPlus, власні рідко змінювані заголовки і т.д.)
У кожен файл проекту вставив три рядки:
#include
#include
#pragma hdrstop

Схожі статті