2011-05-20 38 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 
相關問題