Рассказы из окопов оптимизации: Улучшенное управление удалением кода в Unity 2020 LTS

ALEXANDER DEGTYAREV / UNITY TECHNOLOGIESSenior Software Development Consultant
Oct 25, 2021|10 Мин
Рассказы из окопов оптимизации: Улучшенное управление удалением кода в Unity 2020 LTS
Эта веб-страница была переведена с помощью машинного перевода для вашего удобства. Мы не можем гарантировать точность или надежность переведенного контента. Если у вас есть вопросы о точности переведенного контента, обращайтесь к официальной английской версии веб-страницы.

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

Удаляя код, вы гарантируете, что он не будет скомпилирован или включен в финальную сборку. Хотя это должно немного снизить потребление памяти для проектов, работающих с бэкендом IL2CPP, всегда есть риск пропустить типы и методы во время выполнения (среди прочих проблем) при более высоком уровне зачистки управляемого кода.

В процессе сборки часть кода считается неиспользуемой и, соответственно, удаляется. Ручное добавление необходимых сборок в файл link.xml может оказаться не самым простым способом сохранить их от удаления. Во время обзоров проектов, которые я провожу в рамках своей работы в качестве консультанта по разработке программного обеспечения Unity, я получал вопросы от клиентов о том, как им лучше справляться с управляемой зачисткой кода. Именно поэтому я собрал эти советы и лучшие практики, которые могут улучшить ваш рабочий процесс благодаря поддержке новых атрибутов аннотаций для зачистки управляемого кода.

Управляемое удаление кода с помощью компоновщика Unity

Удаление неиспользуемого кода особенно важно при использовании скриптового бэкенда IL2CPP. Компоновщик Unity, версия компоновщика Mono IL, адаптированная для работы с Unity, выполняет статический анализ для удаления управляемого кода.

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

Статический анализ, используемый компоновщиком для выявления неиспользуемого кода, не может охватить все случаи, когда тип объекта определяется только во время выполнения. Такие случаи приводят к ложноположительным результатам. Хотя при компиляции на класс или метод может не быть никаких ссылок, во время выполнения кода этот класс или метод все равно требуется некоторым частям кода. В этом контексте хорошим примером может служить код, использующий Reflection. Рассмотрим следующий фрагмент:

Хотя это правильный и часто используемый тип кода, компоновщик не знает, действительно ли MyAssembly, MyType и MyMethod используются во время выполнения. Это может привести к их уничтожению и, как следствие, к ошибке во время выполнения. Более подробную информацию можно найти в руководстве по ограничениям стриптиза.

Разработчики, использующие фреймворки Dependency Injection, такие как Zenject, или библиотеки сериализации, такие как Newtonsoft.Json, должны знать, что существует вероятность ложноположительного удаления кода, и должны решать эту проблему соответствующим образом. Вот несколько наиболее распространенных подходов:

  • Компоновщик распознает ряд атрибутов и позволяет аннотировать зависимости, если он не может их определить. Таким образом, вы можете добавить атрибут [Preserve] к сборкам, классам и методам, которые не должны быть удалены.
  • Файл link.xml - это список для каждого проекта, в котором описано, как сохранить сборки, типы и другие объекты кода в них. Вы можете вручную добавить необходимые сборки, классы и методы в link.xml или использовать API UnityEditor GenerateAdditionalLinkXmlFile для генерации файла link.xml в процессе сборки.

Даже в пакете Addressables используется LinkXmlGenerator. Сценарий сборки Addressables просматривает список активов в группах и добавляет типы, используемые активами, в файл link.xml. Он также добавляет типы, используемые Addressables внутри, через Reflection во время выполнения. Просмотрите сценарий сборки по умолчанию, BuildScriptPackedMode.cs, для получения более подробной информации о реализации подобного решения в качестве шага в вашем процессе сборки, например, с помощью Scriptable Build Pipeline.

Unity поддерживает несколько файлов link.xml, расположенных в папке Assets или в одной из ее подпапок. В процессе сборки записи нескольких файлов link.xml объединяются и учитываются компоновщиком.

Что нового в управляемом удалении кода в Unity 2020 LTS

Использование атрибута [Preserve] может потребовать некоторой ручной работы. Но если ваш проект уже работает на Unity 2020 LTS, вы можете использовать ряд новых управляемых атрибутов аннотаций для зачистки кода, чтобы легко и точно отметить сборки, классы и методы, которые не должны быть удалены во время зачистки кода. Вот лишь некоторые из них:

  • RequireAttributeUsagesAttribute: Когда тип атрибута отмечен, все CustomAttributes этого типа также будут отмечены, что уменьшает сложности при работе на уровне High stripping.
  • RequireDerivedAttribute: Когда тип помечен, все типы, производные от этого типа, также будут помечены.
  • RequiredInterfaceAttribute: Когда тип отмечен, все реализации интерфейсов указанных типов будут отмечены.
  • RequiredMemberAttribute: Когда тип отмечен, все его члены с [RequiredMember] будут отмечены. Это делает удаление кода более точным, так как не позволяет объявляющему типу стать неустранимым. Однако обратите внимание, что если сам класс не используется, его члены также будут удалены, несмотря на то, что они отмечены атрибутом [RequiredMember].
  • RequireImplementorsAttribute: Когда тип интерфейса отмечен, все типы, реализующие этот интерфейс, будут отмечены. Поэтому нет необходимости отмечать каждую реализацию. Однако если интерфейс не реализован нигде в кодовой базе, он все равно будет удален, несмотря на то, что отмечен атрибутом [RequireImplementors].

В Unity 2020.1 и 2020.2 инструмент получил обновления API, чтобы соответствовать компоновщику Mono IL. Теперь он может определять некоторые простые шаблоны отражения, а это значит, что если вы обновились до Unity 2020 LTS, у вас будет меньше причин использовать файлы link.xml.

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

Заглядывая в будущее

В рамках нашей цели 2021 года - упростить доставку высококачественных сборок тестировщикам и игрокам - мы не перестаем уделять внимание улучшению рабочих процессов по зачистке кода. Если говорить точнее, то в релизе 2021.2 мы добавили новый уровень управляемого стриминга под названием Minimal. Это будет по умолчанию для бэкенда IL2CPP, так что следите за новостями.

Управляемый уровень вскрытия