Бесплатная консультация
← Все статьи

Build pipeline в Unity: где возникают узкие места и как ускорить сборки

Чем больше становится Unity-проект, тем заметнее время, которое команда тратит не на разработку, а на ожидание. Сначала сборка занимает несколько минут и практически не влияет на рабочий процесс.

Программист за клавиатурой в тёмной комнате

Чем больше становится Unity-проект, тем заметнее время, которое команда тратит не на разработку, а на ожидание. Сначала сборка занимает несколько минут и практически не влияет на рабочий процесс. Через год разработки тот же проект может собираться десятки минут, CI-очередь начинает расти, QA получает новые версии с задержкой, а создание билдов для нескольких платформ превращается в отдельную задачу.

Проблема обычно не в одной конкретной настройке Unity. Build pipeline состоит из множества этапов, и замедление может возникнуть практически на любом из них: получение проекта из репозитория, импорт ассетов, компиляция скриптов, обработка шейдеров, создание Addressables, IL2CPP, упаковка приложения, подпись или загрузка готовой сборки.

Поэтому оптимизация build pipeline начинается не с покупки более мощного build server, а с понимания того, где именно команда теряет время.

Что такое bottleneck в build pipeline

Bottleneck — это этап процесса, который ограничивает скорость всей системы.

Представим, что CI-сборка Unity-проекта занимает 35 минут:

  • 3 минуты — получение проекта из Git;
  • 12 минут — импорт ассетов;
  • 4 минуты — компиляция;
  • 10 минут — создание player build;
  • 4 минуты — упаковка;
  • 2 минуты — загрузка артефактов.

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

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

Поэтому первое правило оптимизации build pipeline — сначала измерять, потом менять.

Почему медленная сборка становится проблемой бизнеса

Время сборки кажется исключительно технической метрикой, пока проект небольшой.

Специалист за монитором с открытым редактором

Но на крупном проекте оно напрямую влияет на стоимость разработки.

Если разработчик должен ждать сборку после каждого значимого изменения, количество быстрых итераций уменьшается. QA позже получает новую версию. Ошибки обнаруживаются не через полчаса после commit, а через несколько часов. Если билд сломался, часть команды может узнать об этом только тогда, когда уже поверх проблемного изменения появилось несколько новых.

Особенно заметно это при активной разработке перед релизом.

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

Поэтому время сборки необходимо рассматривать вместе с другими инженерными метриками: стабильностью CI, частотой неуспешных билдов и временем от commit до версии, которую может протестировать QA.

Bottleneck №1: получение проекта из репозитория

Каждая чистая CI-машина сначала должна получить проект.

Рабочее место разработчика игр на нескольких мониторах

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

Стоит проверить:

  • размер репозитория;
  • историю крупных бинарных файлов;
  • использование Git LFS;
  • ненужные файлы в version control;
  • работу submodules;
  • скорость доступа build machines к репозиторию;
  • необходимость полного checkout для каждой задачи.

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

Поэтому регулярная проверка структуры version control — часть работы с build pipeline.

Bottleneck №2: повторный импорт ассетов

Для крупных Unity-проектов импорт ассетов может занимать значительную часть времени.

Unity обрабатывает текстуры, модели, аудио, шейдеры и другие ресурсы и сохраняет результаты импорта в Library. Если CI каждый раз начинает работу с полностью чистого окружения, значительная часть этих операций выполняется заново.

На небольшом проекте разница может быть почти незаметной. На игре с большим количеством контента повторный импорт способен стать одним из самых дорогих этапов сборки.

Поэтому необходимо проверить стратегию кеширования.

Unity Build Automation, например, поддерживает кеширование Library и рабочего пространства. После первого успешного билда это позволяет повторно использовать уже обработанные данные вместо полного импорта при каждом запуске. При этом стратегия кеширования должна быть настроена осознанно: максимальное кеширование ускоряет последующие сборки, но требует дополнительного дискового пространства.

Важно также понимать разницу между обычным и clean build. Полностью очищать Library перед каждой сборкой «на всякий случай» — простой способ получить воспроизводимое окружение, но одновременно один из способов серьёзно увеличить длительность pipeline.

Bottleneck №3: слишком много изменений инвалидируют кеш

Наличие кеша ещё не означает, что он эффективно работает.

Разработчики за игровыми компьютерами

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

В результате команда видит кеш в конфигурации CI, но сборки всё равно регулярно ведут себя почти как clean build.

Здесь важно анализировать:

  • какие файлы меняются между сборками;
  • какие изменения приводят к массовому reimport;
  • насколько стабилен Library cache;
  • нет ли скриптов, которые изменяют ассеты автоматически;
  • не генерируются ли файлы внутри проекта при каждом CI-запуске;
  • насколько детерминирован сам процесс сборки.

Иногда основная проблема заключается не в Unity и не в производительности сервера, а в собственной автоматизации проекта.

Bottleneck №4: компиляция и архитектура проекта

На крупном Unity-проекте время компиляции постепенно начинает влиять и на Editor, и на CI.

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

Грамотное разделение системы на assembly definitions помогает ограничивать зависимости и уменьшать область, которую необходимо пересобирать после изменения.

Но добавлять asmdef-файлы просто ради количества также не стоит. Структура assemblies должна соответствовать архитектуре проекта.

Обычно имеет смысл разделять крупные независимые части:

  • gameplay;
  • UI;
  • tools;
  • editor code;
  • integrations;
  • analytics;
  • networking;
  • отдельные SDK и модули.

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

Bottleneck №5: шейдеры

Shader compilation может стать отдельной проблемой, особенно на проектах с большим количеством материалов, вариантов шейдеров и сложным rendering pipeline.

Многомониторная станция разработчика с подсветкой

На время сборки влияют не только сами шейдеры, но и количество генерируемых shader variants.

Если комбинации keywords используются без контроля, количество вариантов может быстро расти. В результате увеличиваются:

  • build time;
  • размер сборки;
  • объём кеша;
  • время обработки шейдеров.

При аудите необходимо смотреть не только на производительность шейдеров непосредственно в игре, но и на то, сколько вариантов реально создаётся и сколько из них действительно требуется продукту.

Иногда один неправильно организованный shader способен заметно увеличить длительность сборки.

Bottleneck №6: Addressables и AssetBundles

В проектах с большим количеством контента часто используется Addressables.

Это добавляет ещё один слой в build pipeline: необходимо определить, когда пересобирать контент, какие bundles затронуты изменениями и как работать с кешами.

Если каждый CI-запуск полностью пересобирает Addressables, даже когда контент практически не менялся, время pipeline может значительно увеличиваться.

При этом кеш нельзя просто сохранять бесконечно. Устаревшие данные способны приводить к неправильным или непредсказуемым сборкам, поэтому стратегия должна разделять обычные быстрые билды и ситуации, когда требуется clean content build.

Unity отдельно предусматривает работу с кешами Addressables и Scriptable Build Pipeline в CI. Это позволяет ускорять повторные операции, но при необходимости очищать сгенерированные данные и получать полностью чистую сборку.

Bottleneck №7: IL2CPP и платформенные сборки

Некоторые этапы build pipeline практически невозможно полностью убрать.

Разработчик в наушниках за монитором

Например, IL2CPP добавляет генерацию и компиляцию native-кода. На крупных проектах этот процесс способен занимать заметную часть времени.

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

Нет необходимости создавать полноценный production build после каждого commit.

Можно разделить процессы:

  • быстрые проверки — компиляция, unit tests и базовые проверки;
  • development builds — сборки для команды и QA;
  • полные платформенные билды — по расписанию или после merge в определённую ветку;
  • release builds — отдельный максимально контролируемый pipeline.

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

Bottleneck №8: несколько платформ

Один проект может одновременно выпускаться для Android, iOS, Windows, macOS и консолей.

Если каждый commit запускает все платформы параллельно или последовательно, стоимость CI быстро растёт.

Кроме самого Player Build приходится учитывать platform switching, специфичные SDK, подпись, provisioning, различия в окружении и отдельные этапы публикации.

Для Addressables Unity также рекомендует учитывать особенности CI при работе с разными target platforms: переключение платформ может вызывать повторную компиляцию и другие дополнительные операции.

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

Например, основная development-платформа может собираться после каждого merge, мобильные платформы — несколько раз в день, а дорогие console builds — по отдельным триггерам.

Не всегда нужен clean build

Clean build полезен, когда необходимо исключить влияние старого кеша: например, при подготовке релизной версии или диагностике странной ошибки сборки.

Но использовать его для каждого CI-запуска обычно слишком дорого.

Современный Unity build pipeline умеет повторно использовать части результатов предыдущих сборок. Инкрементальная сборка позволяет не выполнять заново операции для данных, которые не изменились.

При этом кеш действительно иногда становится источником проблем. Unity отдельно предусматривает Clean Build для случаев, когда необходимо отбросить кешированные build data и полностью пересобрать контент и код.

Поэтому правильная стратегия — не «всегда кешировать» и не «всегда всё удалять», а использовать разные режимы для разных задач.

Более мощный сервер не всегда решает проблему

Очевидный способ ускорить CI — увеличить количество CPU, RAM или выбрать более дорогую build machine.

Иногда это действительно помогает.

Но если 15 минут из 30 система ждёт checkout, повторно импортирует неизменившиеся ресурсы или выполняет ненужный clean build, увеличение мощности машины не устранит архитектурную проблему pipeline.

Unity также отмечает, что более производительная конфигурация build machine не гарантирует одинакового прироста скорости для любого проекта: результат зависит от характера конкретной нагрузки.

Поэтому инфраструктуру стоит масштабировать после анализа bottlenecks, а не вместо него.

Как искать узкие места build pipeline

Оптимизацию лучше начинать с разбиения сборки на измеримые этапы.

Например:

Checkout.

Restore dependencies.

Unity startup.

Asset import.

Script compilation.

Tests.

Addressables.

Player build.

Native compilation.

Packaging.

Signing.

Upload.

Deployment.

Для каждого этапа необходимо знать среднее время выполнения.

После этого становится видно, где находится основная потеря времени.

Полезно смотреть не только на одну сборку, а на историю. Если обычно build занимает 15 минут, а периодически — 40, необходимо искать причину именно этих скачков.

Это может быть cache miss, изменение определённой группы ассетов, переключение платформы или нестабильный внешний dependency.

Что проверить перед оптимизацией

Базовый чек-лист может выглядеть так:

  • сколько занимает полный pipeline;
  • какой этап самый долгий;
  • сколько занимает checkout;
  • используется ли кеш Library;
  • насколько часто происходит полный reimport;
  • используется ли incremental build;
  • зачем и как часто запускается clean build;
  • как устроены assemblies;
  • сколько времени занимает shader compilation;
  • как собираются Addressables;
  • какие платформы запускаются после каждого commit;
  • можно ли выполнять часть задач параллельно;
  • нужны ли все проверки для каждой ветки;
  • насколько часто CI падает;
  • сколько времени команда ждёт очередь на build agents.

Последний пункт особенно важен. Иногда сама сборка достаточно быстрая, но разработчик ждёт 20 минут свободный агент. В таком случае bottleneck находится уже не внутри Unity, а в инфраструктуре CI.

Как должен выглядеть оптимизированный pipeline

Универсальной конфигурации нет.

Pipeline мобильной free-to-play игры с постоянным LiveOps будет отличаться от pipeline PC-проекта, который выпускает крупные обновления раз в несколько месяцев.

Но хороший процесс обычно обладает несколькими характеристиками:

  • повторно использует результаты там, где это безопасно;
  • не выполняет дорогие операции без необходимости;
  • быстро сообщает разработчику об ошибке;
  • отделяет быстрые проверки от полного production build;
  • создаёт воспроизводимые сборки;
  • позволяет получить clean build, когда это действительно требуется;
  • хранит логи и метрики каждого этапа;
  • масштабируется вместе с проектом.

Главная цель — не получить минимальное число минут в отчёте CI, а уменьшить время от изменения кода до достоверной обратной связи.

Как NoPanic Studio работает с build pipeline

При подключении к существующему Unity-проекту мы анализируем не только код самой игры, но и процессы вокруг разработки.

Если сборки занимают слишком много времени или CI регулярно становится источником проблем, мы разбиваем pipeline на этапы и определяем реальные bottlenecks: checkout, импорт ассетов, компиляцию, Addressables, создание player builds, платформенные операции или инфраструктуру.

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

Важно не оптимизировать pipeline абстрактно. Изменения должны давать измеримый эффект для команды: быстрее получать сборки, раньше находить ошибки и сокращать время между разработкой функции и её проверкой.

Если сборка Unity-проекта постоянно замедляется, CI занимает десятки минут или создание версий для QA стало отдельной проблемой — пришлите описание проекта и текущего build pipeline. Разберём процесс, найдём основные bottlenecks и предложим план оптимизации.

Следующая статьяПлан сертификации консоли: что делать за два месяца до подачи
→