Во время разговора с Unity о производительности парень сказал нам избегать for each
цикла, чтобы повысить производительность, и вместо этого использовать обычный цикл for. В чем разница между for
и for each
с точки зрения реализации? Что делает for each
неэффективным?
unity
performance
Имад
источник
источник
for each
петля итерация ничего, кроме массива генерирует мусор (версии Unity до до 5,5)Ответы:
Цикл foreach якобы создает объект IEnumerator из коллекции, которую вы передаете, и затем просматривает ее. Итак, такой цикл:
переводит что-то вроде этого:
(исключая некоторые сложности в отношении того, как перечислитель будет позже)
Если это скомпилировано наивно / напрямую, то этот объект перечислителя размещается в куче (что занимает немного времени), даже если его базовый тип является структурой, называемой боксом. После завершения цикла это распределение становится мусором для последующей очистки, и ваша игра может на мгновение заикаться, если накапливается достаточно мусора, чтобы сборщик мог выполнить полную очистку. Наконец,
MoveNext
метод иCurrent
свойство могут включать больше шагов вызова функции, чем просто захват элемента напрямуюcollection[i]
, если компилятор не встроит это действие.Приведенные выше ссылки на документацию по Unity в Hellium указывают на то, что эта наивная форма - это именно то, что происходит в Unity до версии 5.5 , если выполнять итерации по чему-либо, кроме собственного массива.
В версии 5.6+ (включая версии 2017 года) этот ненужный бокс исчез , и вы не должны испытывать ненужного выделения, если используете какой-либо конкретный тип (например,
int[]
илиList<GameObject>
илиQueue<T>
).Вы все равно получите распределение, если рассматриваемый метод знает только, что он работает с каким-то IList или другим интерфейсом - в этих случаях он не знает, с каким конкретным перечислителем он работает, поэтому он должен сначала поместить его в объект IEnumerator. ,
Так что есть хороший шанс, что это старый совет, который не так важен в современном развитии Unity. Единство УБС , как мы можем быть немного суеверны о вещах , которые используются , чтобы быть медленными / волосатой в старых версиях, поэтому принимать какую - либо рекомендацию этой формы с зерном соли. ;) Двигатель развивается быстрее, чем наши убеждения по этому поводу.
На практике у меня никогда не было игр, демонстрирующих заметную медлительность или сбой при сборке мусора при использовании циклов foreach, но ваш пробег может варьироваться. Профилируйте свою игру на выбранном оборудовании, чтобы увидеть, стоит ли беспокоиться об этом. Если вам нужно, довольно просто заменить циклы foreach явными циклами for на более поздних этапах разработки в случае возникновения проблемы, поэтому я бы не стал жертвовать удобочитаемостью и простотой кодирования на ранних этапах разработки.
источник
List<MyCustomType>
иIEnumerator<MyCustomType> getListEnumerator() { }
хорошо, тогда как методIEnumerator getListEnumerator() { }
не будет.Если a для каждого цикла используется для коллекции или массива объекта (т. Е. Массива всех элементов, отличных от примитивного типа данных), GC (Garbage Collector) вызывается для освобождения пространства ссылочной переменной в конце a для каждого цикла.
Принимая во внимание, что цикл for используется для итерации по элементам с использованием индекса, поэтому примитивный тип данных не влияет на производительность по сравнению с не примитивным типом данных.
источник
temp
Переменной здесь может выделяться в стеке, даже если коллекция полна ссылочных типов (так как это просто ссылка на существующие записи уже в куче, не выделяя новую память кучи для хранения экземпляра этого ссылочного типа), так мы не ожидаем, что мусор произойдет здесь. При некоторых обстоятельствах сам перечислитель может быть упакован в кучу и оказаться в куче, но эти случаи применимы и к примитивным типам данных.