2013-02-07 73 views
8

我已經writen,可以總結如下的程序:的Python多的內存使用情況

def loadHugeData(): 
    #load it 
    return data 

def processHugeData(data, res_queue): 
    for item in data: 
     #process it 
     res_queue.put(result) 
    res_queue.put("END") 

def writeOutput(outFile, res_queue): 
    with open(outFile, 'w') as f 
     res=res_queue.get() 
     while res!='END': 
      f.write(res) 
      res=res_queue.get() 

res_queue = multiprocessing.Queue() 

if __name__ == '__main__': 
    data=loadHugeData() 
    p = multiprocessing.Process(target=writeOutput, args=(outFile, res_queue)) 
    p.start() 
    processHugeData(data, res_queue) 
    p.join() 

真正的代碼(尤其是'writeOutput()')是一個複雜得多。 'writeOutput()'只使用這些值作爲它的參數(意思是它沒有引用對象)

基本上,它將一個巨大的數據集加載到內存中並對其進行處理。寫入輸出被委託給一個子進程(它實際上寫入多個文件,這需要很多時間)。 因此,每次處理一個數據項時,都會將其發送到子進程槽res_queue,然後根據需要將結果寫入文件。

子進程不需要以任何方式訪問,讀取或修改由'loadHugeData()'加載的數據。子流程只需要使用主流程通過'res_queue'發送的內容。這導致我的問題和疑問。

在我看來,子進程得到它的巨大數據集的副本(當檢查內存使用'頂')。這是真的?如果是這樣,那麼我怎麼能避免id(本質上使用雙內存)?

我正在使用Python 2.6並且程序在linux上運行。

+0

你可以重構你的代碼來使用迭代器,而不是加載所有loadHugeData?看起來你可以,如果它看起來像加載/進程/入隊/出隊/寫 – sotapme

+0

「hugeData」不幸是一個製表符分隔的txt文件,基本上包含一個稀疏的數組。我需要在處理過程中根據行號「隨機訪問」這些數據。因此將其加載到內存中(使用稀疏陣列特定的優化)可以使處理速度更快。 – FableBlaze

+0

這可能是大規模的過度工程,建議使用'[beanstalkd](https://github.com/earl/beanstalkc/blob/master/TUTORIAL.mkd)之類的東西進行流程整合,但知道知道如果它幫助/縮放/執行。像往常一樣,其他人的問題總是更有趣。 – sotapme

回答

13

multiprocessing模塊實際上基於系統調用fork,該系統調用創建當前進程的副本。由於您在加載fork(或創建multiprocessing.Process)之前的大量數據,子進程會繼承數據的副本。但是,如果您正在運行的操作系統實現COW(寫時複製),則實際上只有物理內存中的數據的一個副本,除非您修改父進程或子進程中的數據(父母和孩子都將共享相同的物理內存頁面,儘管在不同的虛擬地址空間中);即使如此,只會爲更改分配額外內存(以pagesize爲增量)。

在加載大量數據之前,您可以通過調用multiprocessing.Process來避免這種情況。然後,當您在父級中加載數據時,額外的內存分配將不會反映在子進程中。

+1

比我做得好。 Linux是COW,所以父進程寫入數據的時刻,數據將被複制。如果父進程只讀取數據,那麼只有一個數據實例** BUT ** top(我幾乎可以肯定)會顯示數據屬於這兩個進程。 meminfo應該爲內存使用提供更準確的數字。 –

+0

確實。我認爲現在最常見的操作系統是COW(我只是試圖儘可能通用)。很棒的功能,但是在解釋基於進程的內存報告工具(例如top,ps等)的輸出時經常會引起混淆。Linux上的'meminfo'將正確報告,就像Solaris上的'pmap'一樣;沒有關於Windows的想法:) – isedev

+5

還要注意,每個Python對象都包含一個引用計數,只要訪問該對象就會被修改。所以,只要讀取數據結構就可以導致COW複製。 –