Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:无法复制内存使用率测试_Python_Memory_Memory Management_Memory Profiling - Fatal编程技术网

Python:无法复制内存使用率测试

Python:无法复制内存使用率测试,python,memory,memory-management,memory-profiling,Python,Memory,Memory Management,Memory Profiling,我试图复制内存使用测试 本质上,文章声称,给定以下代码片段: import copy import memory_profiler @profile def function(): x = list(range(1000000)) # allocate a big list y = copy.deepcopy(x) del x return y if __name__ == "__main__": function() 援引 python -m m

我试图复制内存使用测试

本质上,文章声称,给定以下代码片段:

import copy
import memory_profiler

@profile
def function():
    x = list(range(1000000))  # allocate a big list
    y = copy.deepcopy(x)
    del x
    return y

if __name__ == "__main__":
    function()
援引

python -m memory_profiler memory-profile-me.py
在64位计算机上打印

Filename: memory-profile-me.py

Line #    Mem usage    Increment   Line Contents
================================================
 4                             @profile
 5      9.11 MB      0.00 MB   def function():
 6     40.05 MB     30.94 MB       x = list(range(1000000)) # allocate a big list
 7     89.73 MB     49.68 MB       y = copy.deepcopy(x)
 8     82.10 MB     -7.63 MB       del x
 9     82.10 MB      0.00 MB       return y
我复制并粘贴了相同的代码,但是我的分析器产生了错误

Line #    Mem usage    Increment   Line Contents
================================================
 3   44.711 MiB   44.711 MiB   @profile
 4                             def function():
 5   83.309 MiB   38.598 MiB       x = list(range(1000000))  # allocate a big list
 6   90.793 MiB    7.484 MiB       y = copy.deepcopy(x)
 7   90.793 MiB    0.000 MiB       del x
 8   90.793 MiB    0.000 MiB       return y
这篇文章可能已经过时了——探查器包或python可能已经改变了。无论如何,我的问题是,在Python3.6.x中

(1)
copy.deepcopy(x)
(如上面代码中所定义)是否应该消耗大量内存

(2) 为什么我不能复制

(3) 如果我在
del x
之后重复
x=list(范围(1000000))
,内存是否会增加与我第一次分配的
x=list(范围(1000000))
相同的数量(如代码的第5行)

copy.deepcopy()
只递归复制可变对象,不复制整数或字符串等不可变对象。正在复制的列表由不可变整数组成,因此
y
副本最终共享对相同整数值的引用:

>>> import copy
>>> x = list(range(1000000))
>>> y = copy.deepcopy(x)
>>> x[-1] is y[-1]
True
>>> all(xv is yv for xv, yv in zip(x, y))
True
因此,复制只需要创建一个包含100万个引用的新列表对象,这个对象在Mac OS X 10.13(64位操作系统)上构建的Python 3.6上占用略多于8MB的内存:

空的
列表
对象需要64个字节,每个引用需要8个字节:

>>> sys.getsizeof([])
64
>>> sys.getsizeof([None])
72
Python列表对象过度分配了增长空间,将
range()
对象转换为列表会使其比使用
deepcopy
时腾出更多的空间进行额外增长,因此
x
更大一些,在再次调整大小之前可以容纳额外的125k对象:

>>> sys.getsizeof(x)
9000112
>>> sys.getsizeof(x) / 2 ** 20
8.583175659179688
>>> ((sys.getsizeof(x) - 64) // 8) - 10**6
125006
虽然复印件只有大约87k的额外空间:

>>> ((sys.getsizeof(y) - 64) // 8) - 10**6
87175
在Python3.6上,我也不能复制文章所说的内容,部分原因是Python已经看到了很多内存管理的改进,部分原因是文章在几个方面是错误的

copy.deepcopy()
关于列表和整数的行为在
copy.deepcopy()
的漫长历史中从未改变过(请参见),即使在Python 2.7上,内存图的解释也是错误的

具体来说,我可以使用Python 2.7重现结果这是我在机器上看到的:

$ python -V
Python 2.7.15
$ python -m memory_profiler memtest.py
Filename: memtest.py

Line #    Mem usage    Increment   Line Contents
================================================
     4   28.406 MiB   28.406 MiB   @profile
     5                             def function():
     6   67.121 MiB   38.715 MiB       x = list(range(1000000))  # allocate a big list
     7  159.918 MiB   92.797 MiB       y = copy.deepcopy(x)
     8  159.918 MiB    0.000 MiB       del x
     9  159.918 MiB    0.000 MiB       return y
现在发生的事情是Python的内存管理系统正在分配一个新的内存块以进行额外的扩展。这并不是说新的
y
list对象占用了将近93MiB的内存,这只是当Python进程为对象堆请求更多内存时,操作系统分配给该进程的额外内存。列表对象本身要小得多

对于实际发生的情况,该方法更加准确:

python3 -m memory_profiler --backend tracemalloc memtest.py
Filename: memtest.py

Line #    Mem usage    Increment   Line Contents
================================================
     4    0.001 MiB    0.001 MiB   @profile
     5                             def function():
     6   35.280 MiB   35.279 MiB       x = list(range(1000000))  # allocate a big list
     7   35.281 MiB    0.001 MiB       y = copy.deepcopy(x)
     8   26.698 MiB   -8.583 MiB       del x
     9   26.698 MiB    0.000 MiB       return y
Python3.x内存管理器和列表实现比2.7中的实现更智能;显然,新的列表对象能够放入现有的可用内存中,这些内存是在创建
x
时预先分配的

我们可以使用a和a测试Python2.7的行为。现在,我们在Python 2.7上也获得了更令人放心的结果:

Filename: memtest.py

Line #    Mem usage    Increment   Line Contents
================================================
     4    0.099 MiB    0.099 MiB   @profile
     5                             def function():
     6   31.734 MiB   31.635 MiB       x = list(range(1000000))  # allocate a big list
     7   31.726 MiB   -0.008 MiB       y = copy.deepcopy(x)
     8   23.143 MiB   -8.583 MiB       del x
     9   23.141 MiB   -0.002 MiB       return y
我注意到作者也感到困惑:


copy.deepcopy
复制两个列表,再次分配~50MB(我不确定50MB-31MB=19MB的额外开销是从哪里来的

(我的粗体强调)

这里的错误是假设Python进程大小中的所有内存更改都可以直接归因于特定对象,但实际情况要复杂得多,因为内存管理器可以根据需要添加(和删除!)内存“竞技场”,即为堆保留的内存块,如果有意义的话,可以在更大的块中这样做。这里的过程是复杂的,因为它取决于。作者发现了一篇关于Python模型的老文章,他们把它误解为最新的;从Python2.5开始,Python不释放内存的说法不再成立

令人不安的是,同样的误解导致作者建议不要使用
pickle
,但实际上,即使在Python 2上,该模块也只添加了一点簿记内存来跟踪递归结构。看见在Python2.7上使用
cPickle
会一次性增加46MiB(将
create_file()
调用增加一倍不会导致内存进一步增加)。在Python3中,内存更改完全消失了

我将与Theano团队就这篇文章打开一个对话,这篇文章是错误的,令人困惑的,而且Python 2.7很快就会完全过时,所以他们真的应该关注Python 3的内存模型。(*)

当您从
range()
创建一个新列表(而不是副本)时,您将看到与第一次创建
x
类似的内存增加,因为除了新的列表对象之外,您还将创建一组新的整数对象。除此之外,Python不会为
range()
操作缓存和重用整数值



(*)附录:我是以萨诺项目开始的。项目同意我的评估,尽管他们还没有更新发布的版本。

第三个问题可以通过自己尝试并查看results@user3483203我不确定包裹是否给了我正确的答案。文件中清楚地说明“请注意,在不同的平台上或使用不同的python版本时,可能会得到不同的结果。“我认为期望相同的结果是不现实的。答案很好!后续问题:当您使用Python3时,为什么在使用DelX时只减少8mb的内存使用量?这是否意味着为
x
分配的内存仍然不可用?@tryingtosolve:因为Python使用引用计数来跟踪可以释放的内存。删除
x
将删除对两个列表对象之一的最后一个引用,每个列表对象都包含~8MB的8字节引用。这两个列表分别引用了相同的100万个整数对象,在
x引用的列表之后Filename: memtest.py

Line #    Mem usage    Increment   Line Contents
================================================
     4    0.099 MiB    0.099 MiB   @profile
     5                             def function():
     6   31.734 MiB   31.635 MiB       x = list(range(1000000))  # allocate a big list
     7   31.726 MiB   -0.008 MiB       y = copy.deepcopy(x)
     8   23.143 MiB   -8.583 MiB       del x
     9   23.141 MiB   -0.002 MiB       return y