Прежде всего, многие люди отталкивают Ольховского, чтобы предположить, что это ни о чем не беспокоит. В некоторых приложениях на некоторых средах очень важно избежать давления сбора.
Компактный каркас сборщика мусора имеет несложную политику; он запускает коллекцию каждый раз, когда выделяется 1000 КБ памяти. Теперь предположим, что вы пишете игру, работающую на компактной платформе, и физический движок генерирует 1 КБ мусора при каждом запуске. Физические движки обычно работают порядка 20 раз в секунду. Так что это 1200 КБ давления в минуту, и, эй, это уже больше, чем одна коллекция в минуту только от физического движка . Если коллекция вызывает заметное заикание в игре, то это может быть неприемлемо. В таком сценарии помогает что-либо , которое вы можете сделать, чтобы уменьшить давление сбора.
Я сам усваиваю это, хотя я работаю на настольном компьютере. У нас есть сценарии в компиляторе, где мы должны избегать давления при сборе, и для этого мы прыгаем через все виды пулов объектов. Ольховский, я чувствую твою боль.
Итак, чтобы перейти к вашему вопросу, как вы можете перебирать коллекцию объединенных объектов, не создавая давления коллекции?
Сначала давайте подумаем, почему давление сбора происходит в типичном сценарии. Предположим, у вас есть
foreach(var node in ActiveNodes) { ... }
Логически это выделяет два объекта. Во-первых, он выделяет перечислимую последовательность, которая представляет последовательность узлов. Во-вторых, он выделяет перечислитель - курсор - который представляет текущую позицию в последовательности.
На практике иногда вы можете немного обмануть и иметь один объект, представляющий как последовательность, так и перечислитель, но у вас все еще есть один выделенный объект.
Как мы можем избежать этого давления сбора? На ум приходят три вещи.
1) Во-первых, не создавайте метод ActiveNodes. Заставьте вызывающую программу перебирать пул по индексу и сами проверяйте, доступен ли узел. Затем последовательность представляет собой пул, который уже выделен, а курсор представляет собой целое число, ни одно из которых не создает нового давления сбора. Цена, которую вы платите - это дублированный код.
2) Как предполагает Стивен, компилятор будет принимать любые типы, которые имеют правильные публичные методы и свойства; они не должны быть IEnumerable и IEnumerator. Вы можете создать свою собственную последовательность изменяемой структуры и объекты курсора, передавать их по значению и избегать давления коллекции. Опасно иметь изменчивые структуры, но это возможно. Обратите внимание, что List<T>
использует эту стратегию для своего перечислителя; изучить его реализацию идей.
3) Распределяйте последовательность и перечислители в куче как обычно и объединяйте их тоже! У вас уже есть стратегия объединения, поэтому нет причины, по которой вы также не можете объединить счетчик. У перечислителей даже есть удобный метод «Reset», который обычно просто генерирует исключение, но вы можете написать собственный объект перечислителя, который использовал его для сброса перечислителя обратно в начало последовательности, когда он возвращается в пул.
Большинство объектов перечисляются только один раз за раз, поэтому в обычных случаях пул может быть небольшим.
(Теперь, конечно, у вас может быть проблема курицы и яйца; как вы собираетесь перечислять пул счетчиков?)