我是一名程序员,使用基于 Linux 的服务器开发多人在线游戏。我们为我们的世界使用“实例化”架构。这意味着每个进入世界区域的玩家都会获得该区域的副本以与他们的党员一起玩,并且独立于在同一区域中玩的所有其他玩家。
在内部,我们为每个实例使用单独的进程。最初,每个实例进程都会启动,仅加载给定区域所需的资源,生成随机地形,然后允许来自玩家的新连接。一个实例使用的内存量通常约为 25 兆,包括资源和随机生成的实体级别。
为了减少实例的内存占用并加快生成时间,我们改为创建一个主实例来加载任何实例可能需要的所有资源(大约 150 兆内存),然后当需要一个新实例时,使用 fork() 函数生成一个新实例并利用写时复制内存共享,以便新实例只需要内存来存储它的“唯一”数据集。构成每个实例的唯一数据的随机生成的关卡和实体的占用空间约为 3-4 兆内存。
不幸的是,内存共享并没有我认为的那么好。很多内存页面似乎变得不共享。
起初,随着我们在 prefork 实例中加载更多数据集,每个 fork 实例所需的内存会下降,但最终会出现一个拐点,在 prefork 中加载更多资产实际上会增加每个 fork 实例使用的数据。
我们得到的最好结果是在分叉前加载大约 80 meg 的数据集,然后让新的实例按需加载其余部分。这导致每个实例大约增加 7-10 兆和 80 兆的固定成本。当然是一个很好的改进,但不是理论上最好的。
如果我加载整个 150 兆的数据集然后分叉,每个分叉的实例使用大约 50 兆的内存!比什么都不做要糟糕得多。
我的问题是,如何将我的所有数据集加载到 prefork 实例中,并确保我只获得每个实例真正唯一的最小数据集作为每个实例的内存占用。
我对这里发生的事情有一个理论,我想知道是否有人能够帮助我确认是这种情况。
我认为这与 malloc free 链有关。prefork 实例的每个内存页中可能都有一些空闲的内存点。如果在随机级别生成期间,分配的某些内容恰好适合页面中的一个空闲位置,那么整个页面将被复制到分叉进程中。
在 Windows 中,您可以创建备用堆,并更改进程使用的默认堆。如果这是可能的,它将消除问题。有没有办法在linux中做这样的事情?我的调查似乎表明你不能。
另一种可能的解决方案是,如果我能以某种方式丢弃现有的 malloc 空闲链,强制 malloc 从操作系统分配新内存以供后续调用。我试图查看 malloc 的实现,看看这是否容易实现,但似乎它可能有些复杂。如果有人对此领域有任何想法或建议从哪里开始使用这种方法,我很想听听。
最后,如果有人对这里可能出现的问题有任何其他想法,我真的很想听听他们的意见。非常感谢!