Повышение эффективности использования fork () и совместного использования памяти при копировании при записи - PullRequest
7 голосов
/ 24 марта 2012

Я программист, разрабатывающий многопользовательскую онлайн-игру с использованием серверов на базе Linux.Мы используем «инстансированную» архитектуру для нашего мира.Это означает, что каждый игрок, входящий в мировую область, получает копию этой области, чтобы играть с членами своей группы, независимо от всех других игроков, играющих в той же области.

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

Чтобы уменьшить объем памяти экземпляров и ускорить время появления, мы изменили наподход, при котором мы создаем один главный экземпляр, который загружает все ресурсы, которые могут понадобиться любому экземпляру (около 150 мегабайт памяти), а затем, когда требуется новый экземпляр, используется функция fork () для порождения нового экземпляра и использования копии- совместное использование памяти при записи, так что новому экземпляру требуется только память для его «уникального» набора данных.Объем случайно сгенерированного уровня и сущностей, составляющих уникальные данные для каждого экземпляра, составляет около 3-4 мегабайт памяти.

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

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

Наилучшие результаты, которые мы получили, - это загрузка около 80 мегабайт предварительно подготовленного набора данных, а затем наличие новых экземпляров, требующих загрузки остальных.,В результате получается около 7-10 мегабайт на экземпляр и 80 мегабайт фиксированной стоимости.Конечно, хорошее улучшение, но не лучшее теоретическое.

Если я загружу весь набор данных 150 мегабайт, а затем разветвлююсь, каждый разветвленный экземпляр использует еще около 50 мегабайт памяти!Значительно хуже, чем просто бездействие.

Мой вопрос: как я могу загрузить все свои наборы данных в экземпляре prefork и убедиться, что я получаю только минимальный набор действительно уникальных для каждого экземпляра данных в качествеобъем памяти для каждого экземпляра.


У меня есть теория относительно того, что здесь происходит, и мне было интересно, сможет ли кто-нибудь помочь мне подтвердить, что это так.

Я думаю, что это связано со свободной цепью malloc.Каждая страница памяти экземпляра prefork, вероятно, имеет несколько свободных мест памяти.Если во время генерации случайного уровня будет выделено что-то, что окажется в одном из свободных мест на странице, то вся эта страница будет скопирована в раздвоенный процесс.

В окнах вы можете создавать альтернативные кучи,и измените кучу по умолчанию, используемую процессом.Если бы это было возможно, это бы решило проблему.Есть ли способ сделать такую ​​вещь в Linux?Мои исследования, кажется, показывают, что вы не можете.

Другое возможное решение было бы, если бы я мог как-то отбросить существующую свободную цепочку malloc, заставив malloc выделять свежую память из операционной системы для последующих вызовов.Я попытался взглянуть на реализацию malloc, чтобы увидеть, будет ли это легко возможно, но казалось, что это может быть несколько сложно.Если у кого-то есть какие-либо идеи в этой области или предложение с чего начать, я хотел бы услышать это.

И, наконец, если у кого-то есть какие-либо идеи о том, что здесь может пойти не так, яочень бы хотел их услышать.Большое спасибо!

1 Ответ

2 голосов
/ 24 марта 2012

В окнах вы можете создавать альтернативные кучи и изменять кучу по умолчанию, используемую процессом.Если бы это было возможно, это бы решило проблему.Есть ли способ сделать такую ​​вещь в Linux?

Я Unix, вы можете просто mmap(2) память обойти malloc в целом.

Я бы также отказался от всего "положиться на корову "вещь.У меня был бы основной процесс mmap немного памяти (80M, 150M независимо от), что-то записывать в него, помечать его только для чтения через mprotect(2) для хорошей меры и взять его оттуда.Это решило бы реальную проблему и не заставило бы вас изменить код в будущем.

...