Изучение памяти с помощью нового пакета Memory Profiler

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

  • Мониторинг памяти вашего приложения
  • Просмотр распределения Unity Objects
  • Обнаружение неправильно настроенных ресурсов
  • Поиск непреднамеренных дублирующихся объектов
  • Сравнение захватов памяти для проверки оптимизаций

Мониторинг памяти вашего приложения

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

Для начала у нас есть сборка примерной игры, запущенной на целевом устройстве. Естественно, важно взять захват памяти игры, работающей на фактическом оборудовании, чтобы увидеть, как она использует доступные ресурсы памяти устройства. Кроме того, память в Unity Editor ведет себя не так, как в Unity runtime, поэтому захват памяти Editor в режиме Play не является хорошим представлением того, как память игры будет выглядеть на устройстве. (Захват памяти Editor подходит для разработки инструментов для Editor, таких как пользовательские окна Editor).

После того, как мы перешли на этап в нашей игре, где мы хотим проанализировать использование памяти, мы прикрепляем Memory Profiler к нашему устройству, используя выпадающее меню в Memory Profiler. Затем мы можем взять захват памяти, как показано ниже.

После открытия этого захвата, Memory Profiler отображает следующие показатели использования памяти нашего приложения на странице Summary в верхней части в виде «Memory Usage On Device«.

Здесь мы видим, что использование памяти нашего приложения составляет 492,5 МБ из доступных 3,50 ГБ. Следующим шагом нам нужно использовать наше лучшее суждение, чтобы определить, является ли это разумной долей физической памяти устройства (ОЗУ), используемой в момент захвата. Помните, что физическая память устройства разделяется всеми запущенными процессами.

Что именно подразумевается под «memory footprint»?

Вы заметите, что этот визуальный индикатор показывает вам общую используемую память. Общая используемая память относится к тому, сколько памяти вашего приложения занимает в физическом оборудовании памяти устройства (ОЗУ). Это является самым ясным показателем того, насколько требовательно ваше приложение к использованию памяти на целевом устройстве по двум причинам. Во-первых, поскольку при увеличении общего использования памяти приложения увеличивается вероятность частых ошибок страницы, когда операционная система должна перемещать виртуальную память в и из физической памяти устройства. Частые ошибки страницы приведут к значительному снижению производительности вашего приложения. И, во-вторых, многие операционные системы используют общее использование памяти вашего приложения для определения его текущего «memory footprint«. Если использование памяти вашего приложения становится слишком высоким, операционная система вытеснит и завершит ваше приложение, вызвав сбой для ваших игроков.

Поэтому вы можете использовать визуальный индикатор Memory Usage On Device в Memory Profiler для вывода приложения на риск проблем с производительностью или прекращения работы операционной системы из-за чрезмерного использования памяти в момент захвата.

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

Просмотр распределения Unity Objects

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

Чтобы увидеть представление Unity Objects, просто выберите вкладку Unity Objects в верхней части Memory Profiler после открытия памятной картины, как показано выше.

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

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

Здесь вы можете видеть, что наш общий размер выделенной памяти «Total Memory In Snapshot» составляет 4,64 ГБ, и что наши Unity Objects занимают 2,37 ГБ из этого. Кроме того, если мы фильтруем таблицу — например, используя функцию поиска, вы заметите, что эта полоса обновляется, отражая результаты нашего поиска. Другими словами, она отображает размер всей памяти, которая в данный момент отображается в таблице. Это помогает сохранять перспективу того, сколько памяти вы исследуете в процентном соотношении к всему снимку, и может помочь определить, где следует инвестировать усилия по оптимизации.

В версии 1.0 Memory Profiler таблица Unity Objects показывает вам выделенную память, или, другими словами, показывает вам все Unity Objects, которые есть в вашем приложении. Мы рассматриваем возможность добавления видимости Resident Memory в этих представлениях в следующем выпуске, что позволит вам увидеть, какие из ваших Unity Objects в настоящее время находятся в физической памяти, и, следовательно, увидеть, какие именно вносят прямой вклад в текущий объем памяти вашего приложения.

Вы можете использовать вкладку All Of Memory, чтобы проверить оставшуюся часть памяти вашего приложения в момент захвата, которая будет включать память за пределами Unity Objects, такие как различные подсистемы Unity, управляемую память (C#) и DLL и исполняемые файлы.

Обнаружение плохо настроенных ресурсов

Представление Unity Objects может помочь нам диагностировать ряд потенциальных проблем. Одна из таких проблем — это обнаружение ресурсов, которые были плохо настроены, что приводит к их потреблению большего объема памяти, чем необходимо.

На снимке экрана ниже вы можете увидеть, что значительная часть наших Unity Objects — это текстуры. Захват сделан в проекте с высокой графической достоверностью, который использует High Definition Render Pipeline и сильно использует визуальные эффекты. Таким образом, с учетом этого контекста, мы ожидаем увидеть большое количество текстур, что и происходит.

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

Итак, мы выбираем одну из этих текстур в таблице, чтобы узнать больше деталей о ней и начать расследование, что может быть причиной такого поведения. Здесь, в представлении «Детали» мы находим объяснение — наша текстура доступна для записи, или «Read/Write Enabled».

Это общая проблема, с которой мы сталкиваемся во многих проектах пользователей: случайно делать текстуру доступной для записи, когда это не нужно, отмечая флаг «Read/Write» в настройках импорта текстуры. Когда текстура имеет этот флаг, ее размер в памяти удваивается. Это происходит потому, что для доступа к данным текстуры на процессоре требуется вторая копия данных текстуры. Очевидный признак этого — общий размер текстуры в два раза больше ожидаемого размера или в два раза больше размера похожих текстур.

После отключения флага «Read/Write» на обеих текстурах и выполнения второго снимка, мы видим, что размер обеих текстур уменьшился вдвое.

Мы исследуем возможность добавления столбца для графической (GPU) памяти в таблицу Unity Objects в будущих версиях, чтобы облегчить поиск случаев, когда Unity Object выделил графическую память, например, как в этом примере.

Поиск непреднамеренных дублирующихся объектов

Одна из распространенных ошибок в проектах Unity — непреднамеренное создание дубликатов Unity Objects. Например, очень легко случайно создать дубликат материала, получив доступ к свойству материала MeshRenderer. Это не только быстро накапливается в этом случае — если, например, это делается для каждого экземпляра определенного MeshRenderer, — но, кроме того, эти динамически созданные материалы должны быть явно уничтожены.

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

Захват ниже был сделан в простой сцене с двумя экземплярами префаба Door, и мы включили фильтр Show Potential Duplicates Only, расположенный под таблицей Unity Objects. Это отфильтровало таблицу, чтобы показать нам только Unity Objects, у которых есть несколько экземпляров с одинаковым именем и размером.

Поскольку у нас есть два экземпляра префаба Door в нашей сцене, мы также имеем, как и ожидалось, два экземпляра всех соответствующих объектов: MeshRenderer, Transform, GameObject и так далее. Однако у нас также есть два экземпляра материала «Door» на нашем изображении выше. Эти экземпляры двери выглядят одинаково в нашей сцене, поэтому ожидается, что они будут использовать один материал. Следовательно, это непреднамеренное дублирование, которое в этом конкретном примере было вызвано доступом к свойству материала MeshRenderer в префабе. При удалении доступа к этому свойству и повторном выполнении снимка дублированный материал больше не присутствует в таблице Unity Objects.

Важно помнить, что этот фильтр просто показывает все Unity Objects, у которых есть несколько экземпляров с одинаковым именем и размером. Для интерпретации того, являются ли потенциальные дубликаты, которые вы видите, ожидаемыми или нет, и вызывают ли они, на самом деле, необходимость в дополнительном исследовании, требуется знание вашего проекта. Рекомендуется обращать внимание на строку Total Memory In Table вверху, которая дает визуальный индикатор того, какая доля выделенной памяти вашего приложения отображается в таблице. Это может помочь вам сохранять перспективу о том, куда инвестировать ваши усилия по оптимизации.

Сравнение захватов памяти для проверки оптимизаций

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

Ниже приведен захват нашей мобильной игры, сделанный во время первого уровня. Мы видим, что самая большая категория Unity Objects — это Texture2D. После открытия этой категории, чтобы проверить, какие из наших текстур являются самыми большими, мы видим, что есть несколько текстур пользовательского интерфейса, которые достаточно большие по сравнению с остальными элементами нашей игры — каждая в несколько мегабайт. Это вызывает подозрение у нас: почему эти текстуры настолько большие, и нужны ли они нам? Чтобы узнать причину, мы можем сначала найти исходный ресурс текстуры в нашем проекте, выбрав текстуру в Memory Profiler и используя кнопку «Выбрать в редакторе», которая выделит исходный ресурс текстуры в нашем окне проекта.

Используя окно Inspector, мы можем увидеть, что все наши проблемные крупные текстуры интерфейса не сжимаются из-за того, что их размеры не являются степенью двойки, как показано текстом «NPOT» (нестепень двойки).

Это объясняет большие размеры этих текстур. Теперь мы можем использовать наши знания о нашем проекте, чтобы уменьшить использование памяти. Мы знаем, что три из этих текстур (управление справкой) всегда отображаются вместе в пользовательском интерфейсе, а также другие три текстуры (существа). Поэтому мы можем предположить с высокой уверенностью, что создание двух атласов спрайтов для каждого набора из трех текстур уменьшит выделенное использование памяти, потому что это позволит их сжимать, не увеличивая количество текстур в памяти.

Чтобы сравнить два снимка, начните с открытия первого снимка. Это «база», с которой мы хотим сравнить. Теперь над открытым снимком выберите вкладку «Сравнить снимки» и выберите второй снимок. Memory Profiler теперь представит сводку, сравнивающую два снимка, как показано ниже.

Чтобы увидеть эффект нашего изменения и проверить, действительно ли оно уменьшило размер выделенной памяти для категории Texture2D нашего приложения, мы можем выбрать вкладку Unity Objects. Здесь нам представлена таблица сравнения, которая показывает типы Unity Object, которые изменились, а также как они изменились между захватами (показано ниже).

Мы можем увидеть, что тип Texture2D в целом уменьшился на 3,6 МБ и имеет на четыре текстуры меньше, чем раньше. Развернув эту категорию, мы можем увидеть удаление наших индивидуальных несжатых спрайт-текстур и добавление двух наших текстур атласа спрайтов, что привело к чистому уменьшению в 3,6 МБ и 4 объектам Texture2D.

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

Заключение

Ознакомившись с этой статьей, вы теперь должны лучше понимать пять ключевых рабочих процессов в новом пакете Memory Profiler. Эти рабочие процессы предназначены для диагностики и исследования проблем, связанных с памятью в вашей игре. Мы надеемся, что пакет Memory Profiler, выпущенный в Unity 2022.2, поможет вам лучше отслеживать, изучать и понимать отпечаток вашей игры в памяти.

Источник — https://blog.unity.com/engine-platform/inspecting-memory-with-the-new-memory-profiler-package

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

четырнадцать + тринадцать =

Прокрутить вверх