[英]Huge cost of Memory in Python
我正在编写一段在Python中使用对象的代码。 我有1.1GB的文件需要解析并转换为对象。
但是, 使用1.1GB的文件 ,它将消耗超过7GB的内存 (我停止了它,因为它可以继续运行……),这是相当大的。 我使用了内存探查器来检查并查看发生了什么,并且...有一个结果示例:
Line # Mem usage Increment Line Contents
================================================
78 30.352 MiB 0.000 MiB @profile
79 def getInfos(listExch):
80
81 30.352 MiB 0.000 MiB SizeTot = 0
82 30.352 MiB 0.000 MiB upListExch = set()
83
84 5325.996 MiB 5295.645 MiB for exch in listExch:
85
86
87 5325.996 MiB 0.000 MiB symbExch = exch.symb
88 5325.996 MiB 0.000 MiB nameExch = exch.name
89 5325.996 MiB 0.000 MiB stList = exch.getStList()
90 5325.996 MiB 0.000 MiB upExch = Exch(symbExch,nameExch)
91
92 7572.309 MiB 2246.312 MiB for st in stList:
93
94 7572.309 MiB 0.000 MiB unexpected = False
95 7572.309 MiB 0.000 MiB symbSt = st.symb
96
97 7572.309 MiB 0.000 MiB filepath = '{0}/{1}.csv'.format(download_path,symbSt)
98
99 7572.309 MiB 0.000 MiB upSt = parseQ(st,filepath)
100 7572.309 MiB 0.000 MiB upExch.addSt(upSt)
101 5325.996 MiB -2246.312 MiB upListExch.add(upExch)
102
103 return upListExch
还有我在下面编写的对象模型:
Exch
是包含一个对象listSt
,并且每个St
包含listQ
对象。
class Exch:
def __init__(self,symb,name):
self.symb = symb
self.name = name
self.listSt = set()
def addSt(self,st):
self.listSt.add(st)
def setStList(self,listSt):
self.listSt = listSt
def getStList(self):
return self.listSt
class St:
def __init__(self,symb,name):
self.symb = symb
self.name = name
self.listQ = set()
def getQList(self):
return self.listQ
def addQ(self,q):
self.listQ.add(q)
class Q:
def __init__(self,date,dataH,dataM,dataL):
self.date = date
self.dataH = dataH
self.dataM = dataM
self.dataL = dataL
我在这里做错了吗? 还是Python不能适应如此大量的数据?
编辑:
输入listExch
包含的列表Exch
对象,并且每个st
到listSt
包含一个空listQ
输出将与输入相同,除了将添加每个st
对象中的每个listQ
。
有一个解析器:
def parseQ(st,filepath):
loc_date,loc_dataH,loc_dataM,loc_dataL = 0,0,0,0
with open (filepath, 'rt') as csvfile:
reader = csv.reader (csvfile,delimiter=',')
row1 = next(reader)
unexpected = False
for idx,el in enumerate(row1):
if (el == 'Date'):
loc_date = idx
elif (el == 'Number High'):
loc_dataH = idx
elif (el == 'Number Medium'):
loc_dataM = idx
elif (el == 'Number Low'):
loc_dataL = idx
else:
log.error('Unexpected format on file {}. Skip the file'.format(filepath))
unexpected = True
break
if (unexpected):
log.error('The file "{}" is not properly set'.format(filepath))
return False
else:
next(reader)
for row in reader:
try:
st.addQ(Q(row[loc_date],row[loc_dataH],row[loc_dataM],row[loc_dataL]))
return st
我一点也不惊讶。
读取CSV会生成一个行列表,每个行都指向一个元素列表。
现在, 每个对象都是一个PyObject
,这意味着它有一个typeref,它使用size_t
,我认为,通常,包含它的列表必须包含其ID(巧合的是,它只是指向PyObject
的指针),所以这就有三分之二size_t
,即你的指针类型的大小,只是一个事实,即有一个元素。 这甚至都没有考虑到元素的“有效负载”也需要一点内存的事实!
在64位计算机上, 每个元素的纯结构开销为128位。 我不知道您的元素看起来如何,但这很可能超出实际内容。
通常,不要这样做。 如果您的数据是表格格式的,请使用numpy加载数据,该数据将没有python列表列表,而只是分配很大的内存区域以转储原始值并在访问它们时计算各个元素的地址,而不是使用Python的路线从一个指针跳到另一个指针。 这样,您还将赢得很多速度。
我还要提到,CSV是用于存储大量数据的特别差的格式。 语言没有正式定义(这就是python的CSV阅读器具有“方言”概念的原因),它在存储浮点数方面效率极低(并且可能不精确),没有机会在不读取所有 N-1的情况下访问N行前几行取决于字符串分析,除非用于更改字符串的长度,否则不能用于就地修改值……总而言之:如果一次读取这些文件,您的状态会很好,然后将它们转换为一些实际的存储表格格式。
“但是CSV是纯文本的,我可以使用文本编辑器阅读”参数并没有真正意义-没有人能够“快速点击” 1GB CSV。 因此,请尝试摆脱CSV文件。 Numpy同样具有本机存储格式,可以满足您的需要,或者使用HDF或任何其他标准格式–或者,如果CSV的所有元素都属于同一类型,则也可以将它们另存为原始字节图像数据–这将是最快,最节省空间的存储方法(但是您必须“从外部”记住数据的结构),除了稀疏性。
编辑 :正如OP所指出的, 这正是他的计划:读取CSV,验证其内容,然后将其存储在数据库中! 好作风。
现在,读取可以逐行进行,因此您可以读取一行(或几行),将数据存储在数据库中,忘记行,获取下一行,依此类推。 验证可以在数据库中存储的数据上进行,可能在数据库的单独“登台”部分中进行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.