Python 蟒蛇3。八叉树实现占用大量内存。如何优化?

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

我开发了游戏服务器,希望在地图上保持实时对象的位置。为此,我使用八叉树算法。但现在我的实现占用了大量RAM,为了测试,我尝试填充了几个映射,即使没有对象,octree也会占用大约1GB+每个对象映射大约1GB(我将所有对象存储在dict中,并根据每个八叉树节点的坐标分别存储GUI列表)

以下是我的实施:

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-x1-y0-y1-z0-z1)
  • 如果节点是叶节点,它还应该保留对象ID列表

    已更新 为了为每个映射构建八叉树,我从db加载映射坐标。作为测试,我们可以使用下一个数据:

    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