Python 蟒蛇3。八叉树实现占用大量内存。如何优化?
我开发了游戏服务器,希望在地图上保持实时对象的位置。为此,我使用八叉树算法。但现在我的实现占用了大量RAM,为了测试,我尝试填充了几个映射,即使没有对象,octree也会占用大约1GB+每个对象映射大约1GB(我将所有对象存储在dict中,并根据每个八叉树节点的坐标分别存储GUI列表) 以下是我的实施:Python 蟒蛇3。八叉树实现占用大量内存。如何优化?,python,algorithm,optimization,memory-management,octree,Python,Algorithm,Optimization,Memory Management,Octree,我开发了游戏服务器,希望在地图上保持实时对象的位置。为此,我使用八叉树算法。但现在我的实现占用了大量RAM,为了测试,我尝试填充了几个映射,即使没有对象,octree也会占用大约1GB+每个对象映射大约1GB(我将所有对象存储在dict中,并根据每个八叉树节点的坐标分别存储GUI列表) 以下是我的实施: class OctreeNode(object): MAX_CHILD_NODES = 8 def __init__(self, **kwargs): sel
class OctreeNode(object):
MAX_CHILD_NODES = 8
def __init__(self, **kwargs):
self.x0 = kwargs.pop('x0')
self.x1 = kwargs.pop('x1')
self.y0 = kwargs.pop('y0')
self.y1 = kwargs.pop('y1')
self.z0 = kwargs.pop('z0')
self.z1 = kwargs.pop('z1')
self.root_node: OctreeNode = None
self.parent_node: OctreeNode = None
self.child_nodes = None
self.objects = None
self.guids = None
def get_root_node(self) -> 'OctreeNode':
return self.root_node
def set_root_node(self, node: 'OctreeNode') -> None:
self.root_node = node
def get_parent_node(self) -> 'OctreeNode':
return self.parent_node
def set_parent_node(self, node: 'OctreeNode') -> None:
self.parent_node = node
def get_child_nodes(self) -> List['OctreeNode']:
return self.child_nodes
def set_child_nodes(self, nodes: List['OctreeNode']) -> None:
self.child_nodes = nodes
def can_contain_child_nodes(self) -> bool:
update_dist = Config.World.Gameplay.update_dist
return ((self.x1 - self.x0) > update_dist and
(self.y1 - self.y0) > update_dist and
(self.z1 - self.z0) > update_dist)
def get_object(self, guid: int):
return self.objects.get(guid, None)
def set_object(self, obj: Union[Unit, Player]) -> None:
if self.get_child_nodes():
node = self._get_nearest_child_node(obj)
node.set_object(obj)
else:
self.objects[obj.guid] = obj
def should_contain_object(self, obj: Union[Unit, Player]) -> bool:
return (self.x0 <= obj.x <= self.x1 and
self.y0 <= obj.y <= self.y1 and
self.z0 <= obj.z <= self.z1)
def _get_nearest_child_node(self, obj: Union[Unit, Player]):
for i in range(0, OctreeNode.MAX_CHILD_NODES):
if self.child_nodes[i].should_contain_object(obj):
return self.child_nodes[i]
我花了很多时间来寻找优化内存使用的方法。因此,我尝试在可能的情况下使用tuple(例如在OctreeBuilder的middle\x
行上)。另外,我使用的是\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
(由于大型代码示例而从上面的代码中删除)。等等但似乎我的优化还不够。目前我的代码无法工作,因为占用了大量内存。请帮我优化一下
另外,要查看完整的代码示例,您可以访问我的项目(开发分支)
注意:我希望(如果可能的话)在我的项目中保留面向对象的方法。所以,若这个问题的答案包含基于类的解决方案,那个将是非常好的
另外,根据@zch的评论,我试图用命名的tuple
替换我的八叉树节点
类,但这种方法只会增加使用的内存
我想保留下一条信息:
x0 = -1277.08
x1 = 3814.58
y0 = 8437.5
y1 = 11831.2
builder = OctreeBuilder(x0=x0, x1=x1, y0=y0, y1=y1, objects=objects)
octree = builder.build()
# attach octree to map object to save it in memory
对象示例:
{'max_rage': None, 'char_class': None, 'min_damage': None, 'stamina': None, 'resistance_arcane': 0, 'max_ranged_damage': None, 'unit_template_id': 11183, 'id': 2897, 'focus': None, 'gender': None, 'max_damage': None, 'intellect': None, 'armor': 20, 'x': 1940.93, 'region_id': 1, 'health': 300, 'max_focus': None, 'level': 1, 'min_offhand_damage': None, 'spirit': None, 'attack_power': None, 'y': -4322.39, 'max_health': 300, 'energy': None, 'unit_flags': None, 'max_offhand_damage': None, 'resistance_fire': 0, 'base_mana': 0, 'z': 27.7612, 'mana': 0, 'max_energy': None, 'display_id': 11686, 'unit_bytes_1': None, 'resistance_nature': 0, 'base_health': 300, 'orientation': None, 'scale_x': 1.0, 'max_mana': None, 'happiness': None, 'native_display_id': 11686, 'mod_cast_speed': None, 'resistance_frost': 0, 'unit_bytes_2': None, 'map_id': None, 'rage': None, 'max_happiness': None, 'faction_template': 35, 'strength': None, 'resistance_shadow': 0, 'ranged_attack_power': None, 'power_type': None, 'race': None, 'agility': None, 'min_ranged_damage': None, '_tracked_guids': set(), '_target': None}
嗯,我已经解决了这个问题。首先,我决定增加节点的最小大小,因此
OctreeBuilder
返回的节点更少。因此,内存使用从2GB减少到200MB。接下来,我从OctreeNode
类中删除了方法。因此,我离开了没有方法的类:
class OctreeNode(object):
__slots__ = (
'x0',
'x1',
'y0',
'y1',
'z0',
'z1',
'parent_node',
'child_nodes',
'guids'
)
def __init__(self, **kwargs):
self.x0: float = kwargs.pop('x0')
self.x1: float = kwargs.pop('x1')
self.y0: float = kwargs.pop('y0')
self.y1: float = kwargs.pop('y1')
self.z0: float = kwargs.pop('z0')
self.z1: float = kwargs.pop('z1')
self.parent_node: Union[OctreeNode, None] = kwargs.pop('parent_node', None)
self.child_nodes: Union[List, None] = None
self.guids: Union[List, None] = None
感谢大家的提示和讨论。如果您有其他优化方法,请在评论中告诉我。不要将类用于节点。使用tuple或namedtuple@zch感谢您的评论。你能澄清一下你的意思吗?现在,我的实现中的每个节点都存储坐标(x,y,z),如果这是结束节点,它还存储对象的ID。(这意味着每个节点还存储其他列表)。您能添加最小实现(如果您想要,在伪代码中)作为答案吗?我的意思是您没有
类八进制编码。相反,您将在叶节点(对象列表)中使用列表,在其他节点(中点的3个坐标和8个子节点)中使用11个元素元组。它的可读性会降低,但占用的内存会减少。另一方面,我不确定你是否在使用oct树的任何好处。看起来你可以建立一个网格。@zch你想说我的算法不正确,所以不是八叉树吗?如果是,您能解释一下我在实现正确的八叉树时遗漏了什么吗?@zch:namedtuple
只是一个创建类的函数。它没有做任何class
已经做不到的事情。您可以通过将所有数据存储在NumPy数组中(使用子节点索引而不是显式引用)来节省更多空间,但这不适用于动态场景。
class OctreeNode(object):
__slots__ = (
'x0',
'x1',
'y0',
'y1',
'z0',
'z1',
'parent_node',
'child_nodes',
'guids'
)
def __init__(self, **kwargs):
self.x0: float = kwargs.pop('x0')
self.x1: float = kwargs.pop('x1')
self.y0: float = kwargs.pop('y0')
self.y1: float = kwargs.pop('y1')
self.z0: float = kwargs.pop('z0')
self.z1: float = kwargs.pop('z1')
self.parent_node: Union[OctreeNode, None] = kwargs.pop('parent_node', None)
self.child_nodes: Union[List, None] = None
self.guids: Union[List, None] = None