Я работаю над ведьмой видеоигры, требуют высокой производительности, таким образом, я пытаюсь установить хорошую стратегию памяти или определенную часть игры, часть, которая является игрой "модель", игровое представление. У меня есть объект, содержащий целое игровое представление, с различными менеджерами внутри для хранения представления последовательным, после игровых правил. Каждый игровой объект в настоящее время сгенерирован определенной для типа фабрикой, таким образом, у меня есть несколько фабрик, которые позволяют мне изолировать и изменять управление памятью тех объектов, как я желаю.
Теперь, я нахожусь в процессе выбора между теми двумя альтернативами:
Теперь, у меня был некоторый опыт реальных миров с, таким образом, я не испытан с B и хотел бы некоторый совет относительно тех решений для длительного проекта. Какое решение кажутся лучше для длительного проекта и почему? (Отметьте: пул действительно необходим в этом случае, потому что игровая модель используется для игры, редактирующей также, таким образом, будет партия выделения/освобождения небольших объектов).
Редактирование для разъяснения: я использую C++, если (это еще не ясно),
Правильный ответ специфичен для вашего проблемного домена. Но в проблемных доменах, которые я работаю, первым обычно является тот, который мы выбираем.
Я делаю код в реальном или близком к реальному времени. Редактирование и воспроизведение аудио в основном. В этом коде мы обычно не можем позволить себе выделять память из кучи вниз в движке воспроизведения. В большинстве случаев, malloc возвращается достаточно быстро, но иногда нет. И это иногда имеет значение.
Так что наши решения - это иметь определенные пулы для определенных объектов, и использовать общий пул для всего остального. Конкретные пулы имеют определенное количество предварительно распределенных элементов, и реализованы в виде связанного списка (на самом деле очереди), поэтому распределение и освобождение никогда не бывает больше, чем пара обновлений указателей и стоимость входа и выхода из критической секции.
В качестве запасного варианта для необычных случаев; когда кому-то нужно выделить из специального пула и он пуст - мы выделим кусок общей памяти (несколько объектов) и добавим его в специальный пул. Как только выделение является частью специального пула, оно НИКОГДА не возвращается в общий пул до тех пор, пока приложение не выйдет или не запустит новый проект.
Хороший выбор исходного размера и максимального размера специальных пулов является важной частью настройки приложения.
Одна из проблем, с которой вы столкнетесь, заключается в том, что реализация STL позволяет предположить, что два аллокатора одного типа эквивалентны. Это является причиной того, что Boost.Pool использует только один пул (технически он использует разные пулы для каждого типа). Т.е. вашим аллокаторам не разрешается иметь никаких нестатических членов в общем случае. Если вы делаете видеоигру и знаете, что ваша реализация STL не имеет этой проблемы, то не волнуйтесь об этом - однако могут быть проблемы с list::splice
и std::swap
на контейнерах.
Для начала не практично использовать stl или boost для любого типа видеоигр. Вы можете быть абсолютно уверены, что в ту секунду, когда вы используете один контейнер stl, ваша память фрагментирована, и ваша производительность безнадежно находится в туалете, по крайней мере, в сравнении с идеалом (так как код большинства людей находится в этой категории, большинство людей никогда не замечают и не могут реально сравнить его ни с чем другим). Я не всегда думал так сильно, но со временем я увидел, что даже пара строк кода - это как маленький гремлин, который в конце концов когда-нибудь причинит вам сильную боль.
Первый метод наиболее распространен, и как тот, кто сделал оба, это, вероятно, единственный способ, который практичен, если ты не хочешь тратить много времени и энергии на проблему, чем она, вероятно, стоит для тебя. Второй способ лучше, потому что он более общий и в то же время может быть приспособлен к вашим точным потребностям, но это много работы, а не то, в чем можно легко прыгнуть.
.Одно из возможных решений - что-то между 1. и 2.
Используйте пулы для малых объектов: один пул на размер объекта. В этом случае вы можете легко найти пул, сохранив указатели в массиве.
А кроме того, вы можете иметь один пул для больших объектов. В этом случае фрагментация менее вероятна, а накладные расходы времени не столь критичны, так как большие объекты не выделяются и распределяются очень часто.
Обратите внимание на boost::pool
. При тестировании производительности boost::pool
проверяется не только выделение, но и дилексирование. Я испытал, что boost::pool
и boost::fast_pool
время дилокации может быть экстремально большим. Мой случай состоял из распределений и де-локаций маленьких объектов разного размера в одном пуле
У меня нет конкретного опыта работы с рассматриваемым менеджером памяти, но вот некоторые общие рекомендации, которые могут помочь:
Вы можете получить лучшее из обоих миров (предполагая, что скорость аналогична), разрабатывая с несколькими пулами, но делая окончательное тестирование и производственный релиз с одним пулом. Таким образом, вы можете обнаружить проблемы с распределением/управлением во время разработки, но при этом извлечь выгоду из потенциально более эффективного использования одного пула.
.Вообще-то, я возьму 2. Могу привести пример из ядра linux. В ядре объекты dentry(directory entry) и inode должны дольше кэшироваться в памяти для лучшей отзывчивости к пользователям. Так как объект inode зависит от файловой системы, каждая файловая система будет создавать свой пул объектов. Еще одна вещь, которую вы можете сделать, если объекты похожи, это абстрагироваться от объектов и хранить общие атрибуты в одном абстрактном объекте, а также хранить специфическую для объекта информацию с помощью контейнера. За полной идеей обращайтесь к приведенному ниже коду.