【发布时间】:2018-10-11 23:19:35
【问题描述】:
我试图了解 python 如何使用内存来估计我一次可以运行多少个进程。现在,我在具有大量内存(约 90-150GB 可用内存)的服务器上处理大文件。
为了测试,我会在python中做一些事情,然后看htop看看是什么用法。
第 1 步:我打开一个 2.55GB 的文件并将其保存为字符串
with open(file,'r') as f:
data=f.read()
使用量为 2686M
第 2 步:我在换行符上拆分文件
data = data.split('\n')
使用量为 7476M
第 3 步:我只保留每 4 行(我删除的三行中的两行与我保留的行的长度相同)
data=[data[x] for x in range(0,len(data)) if x%4==1]
使用量为 8543M
第 4 步:我将其分成 20 个相等的块以通过多处理池运行。
l=[]
for b in range(0,len(data),len(data)/40):
l.append(data[b:b+(len(data)/40)])
使用量为 8621M
第 5 步:我删除数据,使用量为 8496M。
有几件事对我来说没有意义。
第二步,为什么我把字符串改成数组时内存使用量会上升这么多。我假设数组容器比字符串容器大很多?
在第三步中,为什么数据没有显着缩小。我基本上摆脱了 3/4 的数组和数组中至少 2/3 的数据。我希望它会相应地缩小。调用垃圾收集器没有任何区别。
奇怪的是,当我将较小的数组分配给另一个变量时,它使用的内存更少。 使用6605M
当我删除旧对象data:使用6059M
这对我来说似乎很奇怪。任何有关缩小我的记忆足迹的帮助将不胜感激。
编辑
好吧,这让我头疼。显然 python 在幕后做了一些奇怪的事情......而且只有 python。我使用我的原始方法和下面答案中建议的方法制作了以下脚本来演示这一点。数字均以 GB 为单位。
测试代码
import os,sys
import psutil
process = psutil.Process(os.getpid())
import time
py_usage=process.memory_info().vms / 1000000000.0
in_file = "14982X16.fastq"
def totalsize(o):
size = 0
for x in o:
size += sys.getsizeof(x)
size += sys.getsizeof(o)
return "Object size:"+str(size/1000000000.0)
def getlines4(f):
for i, line in enumerate(f):
if i % 4 == 1:
yield line.rstrip()
def method1():
start=time.time()
with open(in_file,'rb') as f:
data = f.read().split("\n")
data=[data[x] for x in xrange(0,len(data)) if x%4==1]
return data
def method2():
start=time.time()
with open(in_file,'rb') as f:
data2=list(getlines4(f))
return data2
print "method1 == method2",method1()==method2()
print "Nothing in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data=method1()
print "data from method1 is in memory"
print "method1", totalsize(data)
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data
print "Nothing in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data2=method2()
print "data from method2 is in memory"
print "method2", totalsize(data2)
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data2
print "Nothing is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
print "\nPrepare to have your mind blown even more!"
data=method1()
print "Data from method1 is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data2=method2()
print "Data from method1 and method 2 are in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data==data2
print "Compared the two lists"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data
print "Data from method2 is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data2
print "Nothing is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
输出
method1 == method2 True
Nothing in memory
Usage: 0.001798144
data from method1 is in memory
method1 Object size:1.52604683
Usage: 4.552925184
Nothing in memory
Usage: 0.001798144
data from method2 is in memory
method2 Object size:1.534815518
Usage: 1.56932096
Nothing is in memory
Usage: 0.001798144
Prepare to have your mind blown even more!
Data from method1 is in memory
Usage: 4.552925184
Data from method1 and method 2 are in memory
Usage: 4.692287488
Compared the two lists
Usage: 4.692287488
Data from method2 is in memory
Usage: 4.56169472
Nothing is in memory
Usage: 0.001798144
对于那些使用 python3 的人来说,它非常相似,除了在比较操作之后没有那么糟糕......
PYTHON3 的输出
method1 == method2 True
Nothing in memory
Usage: 0.004395008000000006
data from method1 is in memory
method1 Object size:1.718523294
Usage: 5.322555392
Nothing in memory
Usage: 0.004395008000000006
data from method2 is in memory
method2 Object size:1.727291982
Usage: 1.872596992
Nothing is in memory
Usage: 0.004395008000000006
Prepare to have your mind blown even more!
Data from method1 is in memory
Usage: 5.322555392
Data from method1 and method 2 are in memory
Usage: 5.461917696
Compared the two lists
Usage: 5.461917696
Data from method2 is in memory
Usage: 2.747633664
Nothing is in memory
Usage: 0.004395008000000006
故事的寓意...python的记忆似乎有点像Monty Python的Camelot...这是一个非常愚蠢的地方。
【问题讨论】:
-
你的内存使用量真的在“usage is 8.543M”位中减少了3个数量级吗?
-
显然不是,正如您在我的编辑中看到的那样;)
-
“为什么当我将字符串更改为数组时内存使用量会增加这么多”您的代码中没有数组。您创建了一个
list对象。之前,您只有一个字符串,它是一个对象。 一切 在 Python 中都是一个对象。对于每个对象,您将拥有大约 40-50 个字节的标准对象开销(取决于系统、python 版本等)。所以现在你有一个巨大的列表,里面有很多小对象。每个指向对象的指针将额外增加 4-8 个字节 -
好的,那么另一个大问题是你在做
[data[x] for x in range(0,len(data)) if x%4==1]。请使用xrange。range实现了整个列表,考虑到您的数据,这可能非常大。 -
@jeffpkamp 正如我之前所说,
gc只是循环引用垃圾收集器。它不会影响您在此处所做的任何事情(除非您没有向我们展示创建引用循环的代码)。无论如何,内存可能会被回收,但是当 Python 进程决定将它还给操作系统时,将取决于很多事情