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