Python deepcopy使用的内存超出了需要

Python deepcopy使用的内存超出了需要,python,python-3.x,python-2.7,memory,deep-copy,Python,Python 3.x,Python 2.7,Memory,Deep Copy,最近我在使用copy.deepcopy时遇到了奇怪的内存使用情况 我有以下代码示例: 导入副本 导入gc 导入操作系统 导入psutil 来自Pypler.asizeof import-asizeof 从人性化导入文件大小 类Foo(对象): __插槽\=[“名称”、“foos”、“条形图”] 定义初始化(self,name): self.name=名称 self.foos={} self.bar={} def add_foo(self,foo): self.foos[foo.name]=foo

最近我在使用
copy.deepcopy
时遇到了奇怪的内存使用情况

我有以下代码示例:

导入副本
导入gc
导入操作系统
导入psutil
来自Pypler.asizeof import-asizeof
从人性化导入文件大小
类Foo(对象):
__插槽\=[“名称”、“foos”、“条形图”]
定义初始化(self,name):
self.name=名称
self.foos={}
self.bar={}
def add_foo(self,foo):
self.foos[foo.name]=foo
def添加栏(自身,栏):
self.bar[bar.name]=bar
定义获取状态(自身):
为self中的k返回{k:getattr(self,k).\uuuu slots\uuuu}
定义设置状态(自身、状态):
对于处于状态的k,v.items():
setattr(自、k、v)
类栏(对象):
__插槽\=[“名称”,“说明”]
定义初始化(自我、姓名、描述):
self.name=名称
self.description=描述
定义获取状态(自身):
为self中的k返回{k:getattr(self,k).\uuuu slots\uuuu}
定义设置状态(自身、状态):
对于处于状态的k,v.items():
setattr(自、k、v)
def get_ram():
返回psutil.Process(os.getpid()).memory\u info()[0]
def get_foo():
sub_foo=foo(“subfo1”)
对于范围(5000)内的i:
sub_foo.add_bar(bar(“BarInSubFoo{}.format(i)”,“BarInSubFoo{}.format(i)))
foo=foo(“foo”)
foo.add_foo(sub_foo)
对于范围(5000)内的i:
foo.add_bar(bar(“barinfo{}.format(i),“barinfo{}.format(i)))
返回foo
def main():
foo=get_foo()
foo_size=asizeof(foo)
gc.collect()
ram1=获取内存()
foo_copy=copy.deepcopy(foo)
gc.collect()
ram2=get_ram()
foo_copy_size=asizeof(foo_copy)
打印(“原始对象大小:{},Ram前:{}\n复制对象大小:{},Ram后:{}\n在Ram中的偏移:{}”。格式(
filesize.naturalsize(foo_size)、filesize.naturalsize(ram1)、filesize.naturalsize(foo_copy_size),
filesize.naturalsize(ram2),filesize.naturalsize(ram2-ram1)
))
如果名称=“\uuuuu main\uuuuuuuu”:
main()
我试图做的是测试程序在
copy.deepcopy
之前和之后使用的内存量。为此,我创建了两个类。 我希望在调用deepcopy后,内存使用量会增加,增加的量等于原始对象的大小。 令人窒息的是,我得到了这些结果:

Original object size: 2.1 MB, Ram before: 18.6 MB
Copied object size: 2.1 MB, Ram after: 24.7 MB
Diff in ram: 6.1 MB
正如您所看到的,内存使用量的差异大约为1。复制对象大小的300%

**这些结果是在Windows1064位上使用Python3.8.5获得的

我试过什么

  • 使用Python2.7运行这个代码示例,结果更奇怪(复制对象大小的500%以上):
  • 在Linux上使用Python3.8和Python2.7运行,分别得到了相同的结果
  • \uuu getstate\uuuu
    中返回元组列表而不是dict得到了更好的结果,但与我的预期相差甚远
  • Foo
    对象中使用列表而不是dicts也获得了更好的结果,但也远远超出了我的预期
  • 使用
    pickle.dumps
    pickle.loads
    复制对象产生了相同的结果

有什么问题吗?

其中一些问题可能是因为
deepcopy
保留了它访问过的所有对象的缓存,以避免陷入无限循环(我很确定是
)。对于这种情况,您可能应该编写自己的高效复制函数
deepcopy
的编写目的是能够处理任意输入,而不一定是为了提高效率

如果你想要一个高效的复制函数,你可以自己编写。这对于深度复制来说已经足够了,其效果如下:

import copy
import gc
import os

import psutil
from pympler.asizeof import asizeof
from humanize import filesize


class Foo(object):
    __slots__ = ["name", "foos", "bars"]

    def __init__(self, name):
        self.name = name
        self.foos = {}
        self.bars = {}

    def add_foo(self, foo):
        self.foos[foo.name] = foo

    def add_bar(self, bar):
        self.bars[bar.name] = bar

    def copy(self):
        new = Foo(self.name)
        new.foos = {k:foo.copy() for k, foo in self.foos.items()}
        new.bars = {k:bar.copy() for k, bar in self.bars.items()}
        return new

class Bar(object):
    __slots__ = ["name", "description"]

    def __init__(self, name, description):
        self.name = name
        self.description = description

    def copy(self):
        return Bar(self.name, self.description)

def get_ram():
    return psutil.Process(os.getpid()).memory_info()[0]

def get_foo():
    sub_foo = Foo("SubFoo1")
    for i in range(5000):
        sub_foo.add_bar(Bar("BarInSubFoo{}".format(i), "BarInSubFoo{}".format(i)))
    foo = Foo("Foo")
    foo.add_foo(sub_foo)
    for i in range(5000):
        foo.add_bar(Bar("BarInFoo{}".format(i), "BarInFoo{}".format(i)))

    return foo

def main():
    foo = get_foo()
    foo_size = asizeof(foo)

    gc.collect()
    ram1 = get_ram()

    foo_copy = foo.copy()

    gc.collect()
    ram2 = get_ram()
    foo_copy_size = asizeof(foo_copy)
    print("Original object size: {}, Ram before: {}\nCopied object size: {}, Ram after: {}\nDiff in ram: {}".format(
        filesize.naturalsize(foo_size), filesize.naturalsize(ram1), filesize.naturalsize(foo_copy_size),
        filesize.naturalsize(ram2), filesize.naturalsize(ram2-ram1)
    ))

if __name__ == "__main__":
    main()

其中一些原因可能是因为deepcopy保留了它访问过的所有对象的缓存,以避免陷入无限循环。对于这种情况,您可能应该编写自己的高效复制函数
deepcopy
是为了能够处理任意输入而编写的,不一定是为了提高效率。复制会将RAM增加6.1 MB。创建原始内存增加了多少?
import copy
import gc
import os

import psutil
from pympler.asizeof import asizeof
from humanize import filesize


class Foo(object):
    __slots__ = ["name", "foos", "bars"]

    def __init__(self, name):
        self.name = name
        self.foos = {}
        self.bars = {}

    def add_foo(self, foo):
        self.foos[foo.name] = foo

    def add_bar(self, bar):
        self.bars[bar.name] = bar

    def copy(self):
        new = Foo(self.name)
        new.foos = {k:foo.copy() for k, foo in self.foos.items()}
        new.bars = {k:bar.copy() for k, bar in self.bars.items()}
        return new

class Bar(object):
    __slots__ = ["name", "description"]

    def __init__(self, name, description):
        self.name = name
        self.description = description

    def copy(self):
        return Bar(self.name, self.description)

def get_ram():
    return psutil.Process(os.getpid()).memory_info()[0]

def get_foo():
    sub_foo = Foo("SubFoo1")
    for i in range(5000):
        sub_foo.add_bar(Bar("BarInSubFoo{}".format(i), "BarInSubFoo{}".format(i)))
    foo = Foo("Foo")
    foo.add_foo(sub_foo)
    for i in range(5000):
        foo.add_bar(Bar("BarInFoo{}".format(i), "BarInFoo{}".format(i)))

    return foo

def main():
    foo = get_foo()
    foo_size = asizeof(foo)

    gc.collect()
    ram1 = get_ram()

    foo_copy = foo.copy()

    gc.collect()
    ram2 = get_ram()
    foo_copy_size = asizeof(foo_copy)
    print("Original object size: {}, Ram before: {}\nCopied object size: {}, Ram after: {}\nDiff in ram: {}".format(
        filesize.naturalsize(foo_size), filesize.naturalsize(ram1), filesize.naturalsize(foo_copy_size),
        filesize.naturalsize(ram2), filesize.naturalsize(ram2-ram1)
    ))

if __name__ == "__main__":
    main()