[英]Python itertools with multiprocessing - huge list vs inefficient CPUs usage with iterator
我處理n個元素(在下面命名為“pair”)變體,重復用作我函數的參數。 顯然,只要“r”列表不足以消耗所有內存,一切都可以正常工作。 問題是我必須最終為6個元素重復16次。 我在雲端使用40核心系統。
代碼看起來如下所示:
if __name__ == '__main__':
pool = Pool(39)
r = itertools.product(pairs,repeat=16)
pool.map(f, r)
我相信我應該使用迭代器而不是預先創建巨大的列表,這里問題開始..
我嘗試使用以下代碼解決問題:
if __name__ == '__main__':
pool = Pool(39)
for r in itertools.product(pairs,repeat=14):
pool.map(f, r)
內存問題消失了,但每個內核的CPU使用率是5%。 現在,代碼的單核版本比這更快。
如果你能引導我一點,我真的很感激..
謝謝。
您的原始代碼不是在您自己的代碼list
預先創建list
( itertools.product
返回生成器),但pool.map
正在實現整個生成器(因為它假設您可以存儲所有輸出,您也可以存儲所有輸入) 。
這里不要使用pool.map
。 如果您需要使用pool.imap
排序結果,或者結果順序不重要,請使用pool.imap_unordered
。 迭代任一調用的結果(不要包裝在list
),並在結果出來時處理結果,並且內存不應成為問題:
if __name__ == '__main__':
pool = Pool(39)
for result in pool.imap(f, itertools.product(pairs, repeat=16)):
print(result)
如果您正在使用pool.map
進行副作用,那么您只需要將其運行完成,但結果和排序無關緊要,您可以通過使用imap_unordered
並使用collections.deque
來有效地排除“結果“沒有實際存儲任何東西( maxlen
為0
的deque
是最快,最低內存的方式來強制迭代器運行完成而不存儲結果):
from collections import deque
if __name__ == '__main__':
pool = Pool(39)
deque(pool.imap_unordered(f, itertools.product(pairs, repeat=16)), 0)
最后,我有點懷疑,指定39名的Pool
工作人員; multiprocessing
在很大程度上有利於CPU綁定任務; 如果你使用的工作人員多於擁有CPU內核並獲得好處,那么multiprocessing
可能會使你在IPC中花費的成本高於獲得的成本,並且使用更多的工作人員只是通過緩沖更多數據來掩蓋問題。
如果您的工作主要受I / O限制,您可以嘗試使用基於線程的池,這將避免酸洗和取消開銷的開銷,以及父和子進程之間的IPC成本。 與基於進程的池不同,Python線程受GIL問題的影響,因此您的CPU綁定在Python中工作(不包括GIL釋放I / O調用, ctypes
調用.dll / .so文件,以及某些第三方擴展,如numpy
釋放用於繁重CPU工作的GIL僅限於單個核心(在Python 2.x中,對於CPU綁定工作,您經常浪費大量的解決GIL爭用和執行上下文切換; Python 3消除了大部分浪費)。 但是如果你的工作在很大程度上受I / O限制,I / O上的阻塞會釋放GIL以允許其他線程運行,因此只要大多數線程在I / O上延遲,就可以擁有許多線程。 它也很容易切換(只要您沒有將程序設計為依賴於每個工作者的單獨地址空間,假設您可以寫入“共享”狀態而不影響其他工作者或父進程),只需更改:
from multiprocessing import Pool
至:
from multiprocessing.dummy import Pool
並且您將獲得池的multiprocessing.dummy
版本,基於線程而不是進程。
第二個代碼示例較慢,因為您要向39個作品池提交一對。 只有一名工人將處理您的請求,另外38名工作人員將無所事事! 會比較慢,因為從主線程到工作進程的管道數據會產生開銷。
您可以“緩沖”一些對,然后執行該對對以平衡內存使用,但仍然可以利用多進程環境。
import itertools
from multiprocessing import Pool
def foo(x):
return sum(x)
cpus = 3
pool = Pool(cpus)
# 10 is buffer size multiplier - the number of pair that each process will get
buff_size = 10*cpus
buff = []
for i, r in enumerate(itertools.product(range(20), range(10))):
if (i % buff_size) == (buff_size-1):
print pool.map(foo, buff)
buff = []
else:
buff.append(r)
if len(buff) > 0:
print pool.map(foo, buff)
buff = []
上面的輸出將如下所示
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 8, 9, 10, 11, 12, 13, 14, 15, 16]
[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 14, 15, 16, 17, 18, 19, 20, 21, 22]
[15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 17, 18, 19, 20, 21, 22, 23, 24, 25]
[18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
使用buff_size
乘數來獲得系統的正確平衡!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.