* Насколько я знаю, Unity3D для iOS основана на среде исполнения Mono, а в Mono есть только GC Mark Mark and Sweep GC.
Эта система GC не может избежать времени GC, которое останавливает игровую систему. Пул экземпляров может уменьшить это, но не полностью, потому что мы не можем контролировать создание экземпляров в библиотеке базовых классов CLR. Эти скрытые маленькие и частые случаи в конечном итоге приведут к недетерминированному времени GC. Периодическое принудительное завершение GC значительно ухудшит производительность (может ли Mono принудительно завершить GC?)
Итак, как я могу избежать этого времени GC при использовании Unity3D без значительного снижения производительности?
unity
performance
Eonil
источник
источник
Ответы:
К счастью, как вы указали, сборки COMPACT Mono используют GC поколения (в отличие от Microsoft, например, WinMo / WinPhone / XBox, которые просто поддерживают плоский список).
Если ваша игра проста, GC должен справиться с ней просто отлично, но вот несколько советов, которые вы могли бы рассмотреть.
Преждевременная оптимизация
Сначала убедитесь, что это действительно проблема для вас, прежде чем пытаться ее исправить.
Объединение дорогих справочных типов
Вам следует объединить ссылочные типы, которые вы часто создаете или которые имеют глубокие структуры. Примером каждого будет:
Bullet
объект в игре пуля-ада .Вы должны использовать в
Stack
качестве пула (в отличие от большинства реализаций, которые используютQueue
). Причина этого в том, чтоStack
если вы возвращаете объект в пул, и что-то еще немедленно его захватывает; у него будет гораздо больше шансов оказаться на активной странице - или даже в кэше процессора, если вам повезет. Это немного быстрее. Кроме того, всегда ограничивайте размер ваших пулов (просто игнорируйте 'checkins', если ваш лимит был превышен).Избегайте создания новых списков, чтобы очистить их
Не создавайте новое,
List
когда вы на самом деле имели в видуClear()
это. Вы можете повторно использовать внутренний массив и сохранить загрузку массивов и копий. Подобно этому, попробуйте создать списки с осмысленной начальной емкостью (помните, что это не предел - просто начальная емкость) - она не должна быть точной, просто оценочной. Это должно относиться в основном к любому типу коллекции - за исключениемLinkedList
.Используйте массивы структур (или списки), где это возможно
Вы получаете небольшую выгоду от использования структур (или типов значений в целом), если вы передаете их между объектами. Например, в большинстве «хороших» систем частиц отдельные частицы хранятся в массиве: массив и индекс передаются вместо самой частицы. Причина, по которой это работает так хорошо, заключается в том, что когда GC нужно собрать массив, он может полностью пропустить содержимое (это примитивный массив - здесь ничего не нужно делать). Поэтому вместо 10 000 объектов GC просто нужно посмотреть на 1 массив: огромный выигрыш! Опять же, это будет работать только с типами значений .
После РойТ. предоставив некоторую жизнеспособную и конструктивную обратную связь, я чувствую, что мне нужно подробнее остановиться на этом. Вы должны использовать эту технику только тогда, когда имеете дело с огромным количеством сущностей (от тысяч до десятков тысяч). Кроме того, это должна быть структура, которая не должна иметь никаких полей ссылочного типа и должна находиться в массиве с явным типом. Вопреки его отзывам, мы помещаем его в массив, который, скорее всего, является полем в классе - это означает, что он собирается занять кучу (мы не пытаемся избежать выделения кучи - просто избегаем работы GC). Мы действительно заботимся о том, что это непрерывный кусок памяти с большим количеством значений, которые GC может просто посмотреть в
O(1)
операции вместоO(n)
операции.Вам также следует распределить эти массивы как можно ближе к запуску приложения, чтобы уменьшить вероятность возникновения фрагментации или чрезмерной работы, когда GC пытается переместить эти фрагменты (и рассмотрите возможность использования гибридного связанного списка вместо встроенного
List
типа). ).GC.Collect ()
Это, безусловно, ЛУЧШИЙ способ выстрелить себе в ногу (см. «Вопросы производительности») с помощью GC поколения. Вы должны вызывать его только тогда, когда вы создали ОЧЕНЬ большое количество мусора - и единственный случай, когда это может быть проблемой, - это сразу после загрузки контента для уровня - и даже тогда вам, вероятно, следует собирать только первое поколение (
GC.Collect(0);
) Надеемся, что предотвратить продвижение объектов в третьем поколении.IDisposable и обнуление поля
Стоит обнулять поля, когда вам больше не нужен объект (особенно на ограниченных объектах). Причина в деталях того, как работает GC: он удаляет только те объекты, которые не являются корневыми (то есть ссылаются), даже если этот объект был бы удален из-за удаления других объектов в текущей коллекции ( примечание: это зависит от GC аромат в использовании - некоторые действительно убирают цепи). Кроме того, если объект переживает коллекцию, он немедленно продвигается до следующего поколения - это означает, что любые объекты, оставленные в полях, будут продвигаться во время коллекции. Каждое последующее поколение экспоненциально дороже собирать (и это происходит нечасто).
Возьмите следующий пример:
Если вы
MyFurtherNestedObject
содержали объект размером в несколько мегабайт, вы можете быть уверены, что ГХ не будет смотреть на него в течение достаточно долгого времени - потому что вы непреднамеренно повысили его до G3. Сравните это с этим примером:Шаблон Disposer помогает настроить предсказуемый способ запроса объектов для очистки их личных полей. Например:
источник
Очень небольшое динамическое создание происходит автоматически внутри базовой библиотеки, если только вы не вызываете то, что требует этого. Подавляющее большинство распределения и освобождения памяти происходит из вашего собственного кода, и вы можете контролировать это.
Насколько я понимаю, вы не можете избежать этого полностью - все, что вы можете сделать, это убедиться, что вы перерабатываете и объединяете объекты, где это возможно, используете структуры вместо классов, избегаете систему UnityGUI и, как правило, избегаете создания новых объектов в динамической памяти во время когда производительность имеет значение.
Вы также можете принудительно выполнять сборку мусора в определенное время, что может или не может помочь: см. Некоторые предложения здесь: http://unity3d.com/support/documentation/Manual/iphone-Optimizing-Scripts.html.
источник
GC.Collect()
= свободная память сейчас, но скорее всего проблемы позже.