[英]Memory usage keep growing with Python's multiprocessing.pool
這是程序:
#!/usr/bin/python
import multiprocessing
def dummy_func(r):
pass
def worker():
pass
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=16)
for index in range(0,100000):
pool.apply_async(worker, callback=dummy_func)
# clean up
pool.close()
pool.join()
我發現 memory 的使用(VIRT 和 RES)一直增長到 close()/join(),有什么解決方案可以擺脫這個嗎? 我用 2.7 嘗試了 maxtasksperchild,但它也沒有幫助。
我有一個更復雜的程序,它調用 apply_async() ~6M 次,在 ~1.5M 點我已經獲得了 6G+ RES,為了避免所有其他因素,我將程序簡化為上述版本。
編輯:
原來這個版本效果更好,感謝大家的意見:
#!/usr/bin/python
import multiprocessing
ready_list = []
def dummy_func(index):
global ready_list
ready_list.append(index)
def worker(index):
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=16)
result = {}
for index in range(0,1000000):
result[index] = (pool.apply_async(worker, (index,), callback=dummy_func))
for ready in ready_list:
result[ready].wait()
del result[ready]
ready_list = []
# clean up
pool.close()
pool.join()
我沒有放任何鎖,因為我相信主進程是單線程的(回調或多或少像我閱讀的每個文檔的事件驅動的東西)。
我將 v1 的索引范圍更改為 1,000,000,與 v2 相同並進行了一些測試 - 對我來說很奇怪 v2 甚至比 v1(33 秒對 37 秒)快約 10%,也許 v1 做了太多內部列表維護工作。 v2 絕對是 memory 使用率的贏家,它從未超過 300M (VIRT) 和 50M (RES),而 v1 曾經是 370M/120M,最好是 330M/85M。 所有數字只是3~4次測試,僅供參考。
我最近遇到了內存問題,因為我多次使用多處理函數,所以它不斷產生進程,並將它們留在內存中。
這是我現在使用的解決方案:
def myParallelProcess(ahugearray):
from multiprocessing import Pool
from contextlib import closing
with closing(Pool(15)) as p:
res = p.imap_unordered(simple_matching, ahugearray, 100)
return res
只需在循環中創建池並在循環結束時使用pool.close()
將其pool.close()
。
使用map_async
而不是apply_async
以避免過多的內存使用。
對於第一個示例,更改以下兩行:
for index in range(0,100000):
pool.apply_async(worker, callback=dummy_func)
到
pool.map_async(worker, range(100000), callback=dummy_func)
在您可以在top
看到其內存使用情況之前,它會在眨眼間完成。 將列表更改為更大的列表以查看差異。 但是請注意,如果map_async
沒有__len__
方法,它會首先將您傳遞給它的可迭代對象轉換為列表以計算其長度。 如果您有一個包含大量元素的迭代器,您可以使用itertools.islice
以較小的塊處理它們。
我在具有更多數據的實際程序中遇到了內存問題,最終發現罪魁禍首是apply_async
。
PS,在內存使用方面,您的兩個示例沒有明顯區別。
我正在處理一個非常大的 3d 點雲數據集。 我嘗試使用多處理模塊來加速處理,但我開始出現內存不足錯誤。 經過一些研究和測試,我確定我填充要處理的任務隊列的速度比子進程清空它的速度要快得多。 我確定通過分塊,或使用 map_async 或其他我可以調整負載的東西,但我不想對周圍的邏輯進行重大更改。
我遇到的愚蠢解決方案是間歇性地檢查pool._cache
長度,如果緩存太大,則等待隊列清空。
在我的主循環中,我已經有了一個計數器和一個狀態代碼:
# Update status
count += 1
if count%10000 == 0:
sys.stdout.write('.')
if len(pool._cache) > 1e6:
print "waiting for cache to clear..."
last.wait() # Where last is assigned the latest ApplyResult
因此,每向池中插入 10k 次,我就會檢查是否有超過 100 萬個操作排隊(主進程中使用了大約 1G 的內存)。 當隊列已滿時,我只等待最后插入的作業完成。
現在我的程序可以運行幾個小時而不會耗盡內存。 當工作人員繼續處理數據時,主進程只是偶爾暫停。
順便說一句,_cache 成員記錄在多處理模塊池示例中:
#
# Check there are no outstanding tasks
#
assert not pool._cache, 'cache = %r' % pool._cache
您可以限制每個子進程的任務數
multiprocessing.Pool(maxtasksperchild=1)
maxtasksperchild
是工作進程在退出並被新的工作進程替換之前可以完成的任務數,以釋放未使用的資源。 默認的 maxtasksperchild 是 None,這意味着工作進程將與池一樣長。 關聯
我不得不結合使用maxtasksperchild
和chunksize
讓事情最終得到控制。 很難說一般情況,因為數據可能會有很大差異。
對於我的情況,我有:
對我有用的是這樣的(必須根據您的數據調整參數):
with Pool(processes=num_processes, maxtasksperchild=10) as pool:
results = pool.starmap(
process_feature,
[(idx, feature) for idx, feature in enumerate(features)],
chunksize=100,
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.