簡體   English   中英

具有多處理功能的Python itertools - 巨大的列表與使用迭代器的低效CPU使用率

[英]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預先創建listitertools.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來有效地排除“結果“沒有實際存儲任何東西( maxlen0deque是最快,最低內存的方式來強制迭代器運行完成而不存儲結果):

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM