【发布时间】:2012-12-02 00:14:43
【问题描述】:
我试图处理将大量腌制数据分小块写入磁盘的问题。下面是示例代码:
from cPickle import *
from gc import collect
PATH = r'd:\test.dat'
@profile
def func(item):
for e in item:
f = open(PATH, 'a', 0)
f.write(dumps(e))
f.flush()
f.close()
del f
collect()
if __name__ == '__main__':
k = [x for x in xrange(9999)]
func(k)
open() 和 close() 放置在循环内,以排除内存中数据累积的可能原因。
为了说明问题,我附上了使用 Python 3d 派对模块 memory_profiler 获得的内存分析结果:
Line # Mem usage Increment Line Contents
==============================================
14 @profile
15 9.02 MB 0.00 MB def func(item):
16 9.02 MB 0.00 MB path= r'd:\test.dat'
17
18 10.88 MB 1.86 MB for e in item:
19 10.88 MB 0.00 MB f = open(path, 'a', 0)
20 10.88 MB 0.00 MB f.write(dumps(e))
21 10.88 MB 0.00 MB f.flush()
22 10.88 MB 0.00 MB f.close()
23 10.88 MB 0.00 MB del f
24 collect()
在循环执行期间,内存使用量出现了奇怪的增长。怎样才能消除?有什么想法吗?
当输入数据量增加时,这些额外数据的量会增长到比输入大得多的大小(更新:在实际任务中,我得到 300+Mb)
还有更广泛的问题 - 存在哪些方法可以在 Python 中正确处理大量 IO 数据?
更新: 我重写了代码,只留下循环体来查看具体何时发生增长,结果如下:
Line # Mem usage Increment Line Contents
==============================================
14 @profile
15 9.00 MB 0.00 MB def func(item):
16 9.00 MB 0.00 MB path= r'd:\test.dat'
17
18 #for e in item:
19 9.02 MB 0.02 MB f = open(path, 'a', 0)
20 9.23 MB 0.21 MB d = dumps(item)
21 9.23 MB 0.00 MB f.write(d)
22 9.23 MB 0.00 MB f.flush()
23 9.23 MB 0.00 MB f.close()
24 9.23 MB 0.00 MB del f
25 9.23 MB 0.00 MB collect()
dumps() 似乎会占用内存。 (虽然我实际上认为它会是 write())
【问题讨论】:
-
首先,您只有 11MB。你确定真的有问题吗?您是否真的尝试过大量数据,看看它是否会线性增长到某个可怕的水平?其次,增量发生在
for循环上(所以大概在item.__next__内),而不是dumps行。 (如果您确实认为这是酸洗,为什么不尝试将dumps和write拆分为单独的步骤?) -
另外,
memory_profiler说它“通过向操作系统内核查询当前进程分配的内存量来获取内存消耗,这可能与实际分配的内存量略有不同由 Python 解释器使用”。事实上,它可能是方式,方式不同!仅仅因为 Python 调用free并不一定意味着平台的分配器会立即将其全部释放给操作系统——事实上,它保留页面映射而不释放它们是完全合理的。 -
对于您的“广泛问题”:这取决于您所说的大。但是两个基本策略是:不要使用太多(例如,使用
ints 的numpy数组而不是 Python 对象的lists 的名称list),或者使用数据库(@ 987654337@ 或sqlite3) 而不是构建一个巨大的内存存储并将其整体保存到磁盘。 -
查看streaming-pickle,据说它会为你正在做的事情使用更少的内存。
-
@GillBates:你有没有测试过,例如,只是将你的数据存储在
shelve中,看看它是否真的确实以这种方式使用内存,而不是假设它必须因为它使用pickle?此外,您的峰值数据使用量实际上是否超出了您的界限(或者,如果您使用的是 64 位,则将您扔进交换 thrash 地狱)? Python中有一些用例在空间上似乎是线性的,但实际上只是线性到某个恒定限制,之后它们就会变平(通过重用相同的存储)。特别是如果您使用的平台通常不会向内核释放内存并且您是从外部进行测量的。
标签: python performance file-io pickle persistent