2011-05-20 111 views
5

试图将文件加载到python。这是一个非常大的文件(1.5Gb),但我有可用的内存,我只想做一次(因此使用python,我只需要对文件进行一次排序,所以python是一个简单的选择)。为什么加载这个文件需要这么多的内存?

我的问题是,加载此文件导致方式以内存使用量过多。当我将大约10%的内容加载到内存中时,Python已经在使用700Mb,这显然太多了。大约50%的脚本挂起,使用3.03 Gb的实际内存(并缓慢上升)。

我知道这不是排序文件(记忆方式)最有效的方法,但我只是想让它工作,所以我可以继续解决更重要的问题:D那么,下面的python有什么问题代码是造成了大量的内存使用情况:

print 'Loading file into memory' 
input_file = open(input_file_name, 'r') 
input_file.readline() # Toss out the header 
lines = [] 
totalLines = 31164015.0 
currentLine = 0.0 
printEvery100000 = 0 
for line in input_file: 
    currentLine += 1.0 
    lined = line.split('\t') 
    printEvery100000 += 1 
    if printEvery100000 == 100000: 
     print str(currentLine/totalLines) 
     printEvery100000 = 0; 
    lines.append((lined[timestamp_pos].strip(), lined[personID_pos].strip(), lined[x_pos].strip(), lined[y_pos].strip())) 
input_file.close() 
print 'Done loading file into memory' 

编辑:万一有人是不确定,一般的共识似乎是,分配每个变量吃掉越来越多的内存。我在这种情况下通过1)调用readLines(),它仍然加载所有数据,但每行只有一个“字符串”变量开销。这使用大约1.7Gb加载整个文件。然后,当我调用lines.sort()时,我将一个函数传递给在选项卡上分割的键并返回右列值,并将其转换为int。这在计算上是缓慢的,并且总体上需要大量的内存,但它起作用。学习了今天关于变量分配overhad的一吨:D

+0

我想象的,因为名单占用更多的内存空间比其部分的总和。 – 2011-05-20 04:16:31

+0

不够公平,但我们正在谈论〜比我预期消耗的内存多5倍。我不认为他们花了太多的额外! – Hamy 2011-05-20 04:18:45

+0

@哈米耶,它对我来说似乎有点多。 – 2011-05-20 04:20:43

回答

3

下面是基于从您的示例派生的常量所需内存的粗略估计。至少你必须为每个分割线计算Python内部对象的开销,加上每个字符串的开销。

据估计9.1 GB存储在内存中的文件,假设下面的常量,它通过一个位是关闭的,因为你只使用每一行的一部分:

  • 1.5 GB的文件大小
  • 31164015条总线
  • 每行分成一个列表与4个

代码:

import sys 
def sizeof(lst): 
    return sys.getsizeof(lst) + sum(sys.getsizeof(v) for v in lst) 

GIG = 1024**3 
file_size = 1.5 * GIG 
lines = 31164015 
num_cols = 4 
avg_line_len = int(file_size/float(lines)) 

val = 'a' * (avg_line_len/num_cols) 
lst = [val] * num_cols 

line_size = sizeof(lst) 
print 'avg line size: %d bytes' % line_size 
print 'approx. memory needed: %.1f GB' % ((line_size * lines)/float(GIG)) 

返回:

avg line size: 312 bytes 
approx. memory needed: 9.1 GB 
+0

非常有趣,虽然我相当有信心这个数学是不正确的,即使最后的答案(和点),需要更多的空间,确实是正确的。我怀疑的原因是我可以调用readlines()并加载整个文件,使用大约1.5Gb,这似乎是您要使用的内存模型(基于sizeof的def)。一堆线对象(字符串),每个包含多个字符 – Hamy 2011-05-20 05:13:51

+0

编辑:使用readLines()需要大约1.7Gb的加载,所以它看起来似乎是罪魁祸首是变量分配 – Hamy 2011-05-20 05:20:01

+0

9.1GB的估计是一个高水马克假设你使用每一行的100%(你正在做一些拆分和索引单个字段,所以我不知道结果字段的长度)。因此,如果您平均每条线只使用2/3,导致使用的容量大约为6GB,与50%观察时的3GB一致。 – samplebias 2011-05-20 06:03:52

1

我不知道内存使用情况的分析,但你可以试试这个得到它而不会耗尽内存工作。你将排序到一个新的文件,这是使用内存映射访问(我已经被认为这将有效地工作[在内存方面])。 Mmap具有一些操作系统特定的工作方式,我在Linux上进行了测试(规模很小)。

这是基本代码,为了使它运行时间效率很高,您可能希望对排序后的文件执行二进制搜索以找到插入该行的位置,否则可能需要很长时间。

你可以在this question找到一个寻找文件的二进制搜索算法。

希望通过行排序一个巨大的文件的内存有效的方式:

import os 
from mmap import mmap 

input_file = open('unsorted.txt', 'r') 
output_file = open('sorted.txt', 'w+') 

# need to provide something in order to be able to mmap the file 
# so we'll just copy the first line over 
output_file.write(input_file.readline()) 
output_file.flush() 
mm = mmap(output_file.fileno(), os.stat(output_file.name).st_size) 
cur_size = mm.size() 

for line in input_file: 
    mm.seek(0) 
    tup = line.split("\t") 
    while True: 
    cur_loc = mm.tell() 
    o_line = mm.readline() 
    o_tup = o_line.split("\t") 
    if o_line == '' or tup[0] < o_tup[0]: # EOF or we found our spot 
     mm.resize(cur_size + len(line)) 
     mm[cur_loc+len(line):] = mm[cur_loc:cur_size] 
     mm[cur_loc:cur_loc+len(line)] = line 
     cur_size += len(line) 
     break 
相关问题