﻿# Ускоряем сборку и анализ при помощи Incredibuild

"Да сколько ты ещё будешь собирать?" – фраза, которую каждый разработчик произносил хотя бы раз посреди ночи\. Да, сборка бывает долгой и от этого никуда не деться\. Нельзя же просто так взять и распараллелить всё это дело не на каких\-то жалких 8–12 ядер, а так, чтобы на 100\+\. Или всё\-таки можно?

![0826_IncrediBuild_ru/image1.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image1.png)

## Мне нужно больше ядер\!

Как вы могли заметить, речь сегодня пойдёт как раз о том, как можно ускорить компиляцию\. И нет, в этот раз будут рассмотрены не какие\-то специфичные механизмы, а самое обычное распараллеливание\. Ну, тут всё, казалось бы, просто – выставляем доступное физически количество ядер, нажимаем на build и идём пить условный чай\.

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

"Взять бы у этих бездельников ядра", – могли вы подумать\. И правильно бы сделали, ведь это вполне себе можно реализовать\. Но не нужно, конечно, принимать мои слова близко к сердцу и вооружаться паяльником\. Впрочем, это уже на ваше усмотрение :\)

## Отдай\!

Так как реквизировать машины коллег нам вряд ли кто\-либо даст, будем пользоваться обходными путями\. Даже если у вас и получилось убедить своих коллег поделиться железом, всё равно пользы от лишних процессоров у вас не будет, разве что можете выбрать себе тот, который пошустрее\. А нам нужно то решение, которое каким\-то образом позволит запускать дополнительные потоки сборки на удалённых машинах\. 

Благо среди тысяч категорий всякого полезного ПО, затесалась и нужная нам – [система распределённой сборки](https://ru.wikipedia.org/wiki/Автоматизация_сборки)\. Программы этой категории делают именно то, что нам было и нужно: выдают на время простаивающие ядра коллег и при этом делают это ~~без их ведома~~ в автоматическом режиме\. Разве что сперва нужно будет поставить всё это на их машины, но об этом немного позже\.\.\.

## На ком будем проверять?

Для того чтобы убедиться, что всё функционирует действительно хорошо, нужно было найти качественного подопытного\. Так как у нас уже не раз были в статьях и [Chromium](https://pvs-studio.ru/ru/blog/posts/cpp/0552/), и [Linux](https://pvs-studio.ru/ru/blog/posts/cpp/0460/), а выделиться как\-то хотелось, нужно было найти что\-нибудь новое\.\.\. Поэтому я пошёл в сторону открытых игр \(а где же ещё искать большие проекты?\)\. И, как вы увидите ниже, очень пожалел об этом решении\.

Впрочем, поиск чего\-то объёмного труда не составил, да и мне "повезло" повстречать открытый проект на Unreal Engine\. Вдуматься только\! Я действительно до момента написания этой статьи и подумать не мог, что на UE бывает Open Source™\.

Итак, герой этой статьи: Unreal Tournament\. Подробности [тут](https://www.unrealengine.com/en-US/ue4-on-github)\.

![0826_IncrediBuild_ru/image2.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image2.png)

## Да будет сборка на 100\+ ядер\!

В качестве примера [системы распределённой сборки](https://ru.wikipedia.org/wiki/Автоматизация_сборки) я, пожалуй, остановлюсь на [Incredibuild](https://www.incredibuild.com/)\. Не то чтобы у меня был большой выбор – у нас была уже лицензия Incredibuild на 20 машин\. Нет, есть, конечно, открытый [distcc](https://github.com/distcc/distcc), но он не так прост в настройке, да и к тому же практически все машины у нас под Windows\.

Итак, первым делом нужно поставить агентов на машины других разработчиков\. Есть два способа:

* попросить коллег в местном Slack;
* воззвать к силам сисадмина\.

Разумеется, как и любой другой наивный человек, я написал сперва в Slack\.\.\. Спустя пару дней еле\-еле дошло до 12 машин из 20\. После этого я воззвал к силам сисадмина, и, о чудо, заветная двадцатка была у меня в руке\. Так что теперь у меня было около 145 ядер \(\+/\- 10\) :\)

За исключением необходимости поставить агентов \(делается это парой кликов в установщике\), нужно было поставить себе координатора\. Это делается немного сложнее, поэтому оставлю [ссылку на доки](https://incredibuild.atlassian.net/wiki/spaces/IUM/pages/1498185796/Installing+the+Coordinator+with+an+Agent)\.

Итак, у нас теперь есть сетка на стероидах, поэтому пришло время добраться до Visual Studio\. Выбираем в плагине сборку\.\.\. А вот и нет :\)

Если вдруг вы и сами захотите попробовать, то учтите, что сперва нужно собрать проекты _ShaderCompileWorker_ и _UnrealLightmass_\. Так как они небольшие, я собрал их локально\. Вот теперь уже можно нажать на заветную кнопку:

![0826_IncrediBuild_ru/image3.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image3.png)

## Итак, какая же получилась разница?

![0826_IncrediBuild_ru/image4.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image4.png)

Как видите, нам удалось ускорить сборку с 30 минут до почти 6\! Очень даже нехило\. Кстати, запуск проводился посреди рабочего дня, так что примерно таких цифр можно ожидать и не на синтетическом тесте\. Впрочем, от проекта к проекту разница может быть разной\.

## Что ещё можно ускорить?

Помимо сборки можно натравить Incredibuild на любую тулзу, которая плодит много подпроцессов\. Как вы, может, обратили внимание, я работаю в PVS\-Studio\. Поэтому, конечно же, я не могу обойти стороной возможность скормить ему и наш анализатор\.

Профит в быстром анализе примерно такой же, как и в быстрой сборке: возможность локальных прогонов перед коммитом\. Конечно, всегда есть желание залить всё сразу в мастер; но обычно тимлид бывает не в восторге от подобных действий, особенно когда валятся ночные прогоны на сервере\.\.\. Поверьте мне – я валил :\(

Настраивать особенно анализатор не нужно, разве что нам не повредит указать старые\-добрые 145 потоков в настройках:

![0826_IncrediBuild_ru/image5.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image5.png)

Ну и стоит указать местной сборочной системе, кто тут анализатор:

![0826_IncrediBuild_ru/image6.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image6.png)

_Подробности _[_тут_](https://pvs-studio.ru/ru/blog/posts/0666/)

Итак, пришло время нажать на сборку ещё раз и насладиться ускорением:

![0826_IncrediBuild_ru/image8.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image8.png)

Вышло около семи минут, что подозрительно похоже на время сборки\.\.\. Тут я и подумал, что видимо забыл добавить флаг\. Зашёл в настройки и увидел, что всё на месте\.\.\. Такого я не ожидал, поэтому отправился курить мануалы\.

## Попытка запуска PVS\-Studio \#2

Спустя какое\-то время я вспомнил про версию Unreal Engine, которая используется в этом проекте:

![0826_IncrediBuild_ru/image10.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image10.png)

Не то чтобы это плохо само по себе, но флаг \-StaticAnalyzer появился много позже\. Поэтому интегрироваться напрямую не то чтобы было возможно\. Примерно на этом моменте я уже начал задумываться о том, чтобы забить на всё это дело и уйти пить кофе\.

После пары кружек бодрящего напитка у меня возникла мысль всё\-таки дочитать [туториал по интеграции](https://pvs-studio.ru/ru/blog/posts/0666/) до конца\. Помимо указанного выше способа есть ещё и мониторинг компиляции\.  Это как раз тот вариант, когда ничего уже не помогает\.

Первым делом включим сервер мониторинга:

```cpp
CLMonitor.exe monitor
```

Эта штука будет крутиться на фоне и следить за ~~тобой~~ вызовами компилятора\. Но она не может отслеживать происходящее в Incredibuild, поэтому придётся один раз собрать без него\.

На фоне предыдущего запуска локальная пересборка выглядит очень бедно:

```cpp
Total build time: 1710,84 seconds (Local executor: 1526,25 seconds)
```

Теперь сохраним то, что насобирали в отдельный файл:

```cpp
CLMonitor.exe saveDump -d dump.gz
```

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

Сам же анализ запускается вот этой командой:

```cpp
CLMonitor.exe analyzeFromDump -l UE.plog -d dump.gz
```

Только не стоит его так запускать, ведь мы же хотим его запустить под Incredibuild\. Так что закинем эту команду в _analyze\.bat\._ И создадим рядом файл _profile\.xml_:

```cpp
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Profile FormatVersion="1">
  <Tools>
    <Tool Filename="CLMonitor" AllowIntercept="true" />
    <Tool Filename="cl" AllowRemote="true" />
    <Tool Filename="PVS-Studio" AllowRemote="true" />
  </Tools>
</Profile>
```

_Подробности [тут](https://pvs-studio.ru/ru/docs/manual/0041/)_

И теперь мы можем запустить всё с нашими 145 ядрами:

```cpp
ibconsole /command=analyze.bat /profile=profile.xml
```

И как это выглядит в Build Monitor:

![0826_IncrediBuild_ru/image12.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image12.png)

_Что_\-_то_ _много_ _ошибок_ _на_ _этом_ _графике,_ _не_ _так_ _ли?_ 

К нам закралась ещё одна проблема\. И в этот раз дело не в том, что кто\-то что\-то не поддерживает\. Сборка Unreal Tournament оказалась несколько специфичной\. 

## Попытка запуска PVS\-Studio \#3

Если посмотреть внимательно, то это не ошибки анализа, а [неудачи при препроцессировании исходников](https://pvs-studio.ru/ru/docs/warnings/v008/)\. Причём, причина данного фейла была одна и та же:

```cpp
....\Build.h(42): fatal error C1189: #error: Exactly one of [UE_BUILD_DEBUG \
UE_BUILD_DEVELOPMENT UE_BUILD_TEST UE_BUILD_SHIPPING] should be defined to be 1
```

Так в чём проблема? Всё довольно просто – препроцессор требует, чтобы только один из следующих макросов имел значение 1:

* UE\_BUILD\_DEBUG;
* UE\_BUILD\_DEVELOPMENT;
* UE\_BUILD\_TEST;
* UE\_BUILD\_SHIPPING\.

Да, вроде как всё собиралось раньше, а теперь что\-то страшное вылетело\. Пришлось закопаться в логи, а точнее – в дамп компиляции\. Там\-то проблема и нашлась\. Дело было в том, что эти макросы объявляются в местном _precompile header_, а мы хотим только препроцессировать\. Так что пришлось добавить все эти макросы вручную:

```cpp
#ifdef PVS_STUDIO

#define _DEBUG
#define UE_BUILD_DEVELOPMENT 1

#define WITH_EDITOR 1
#define WITH_ENGINE 1
#define WITH_UNREAL_DEVELOPER_TOOLS 1
#define WITH_PLUGIN_SUPPORT 1

#define UE_BUILD_MINIMAL 1

#define IS_MONOLITHIC 1
#define IS_PROGRAM 1

#define PLATFORM_WINDOWS 1

#endif
```

_Самое начало файла build\.h_

И уже с этим небольшим ~~костылём~~ элегантным решением можно запустить анализ\. Причём сборка не сломается, так как мы воспользовались макросом PVS\_STUDIO\.

Итак, долгожданные результаты анализа:

![0826_IncrediBuild_ru/image13.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image13.png)

Согласитесь, почти 15 минут вместо двух с половиной часов – это очень хорошее ускорение\. Сложно представить, чтобы вы могли пить кофе 2 часа кряду и ни у кого не возникло бы сомнений на ваш счёт\. А вот 15\-минутный перекур вопросов не вызывает\. Наверное\.\.\.

## И что у нас в итоге?

Разумеется, что в идеальном мире увеличение количества потоков в _N_ раз увеличило бы скорость сборки в _N_ раз\. Но живём мы в совершенно ином мире, поэтому стоит учитывать локальную нагрузку на агентов \(удалённые машины\), нагрузку на сеть, время на организацию всего этого дела и ещё много деталей, которые скрыты под капотом\.

Впрочем, ускорение действительно есть, и местами оно позволяет не просто запускать полную сборку или же анализ раз в день, а делать это куда чаще\. Например, после каждого фикса или же перед коммитами\. А теперь предлагаю посмотреть на то, как это всё выглядит в одной таблице:

![0826_IncrediBuild_ru/image15.png](https://import.viva64.com/docx/blog/0826_IncrediBuild_ru/image15.png)

_Я запустил по пять раз и посчитал среднее по запускам \(эти цифры вы и видели в графиках\) :\)_