2013-04-20 25 views
1

我遇到了python多處理Pool類的一些意外行爲。multiprocessing.pool上下文和負載平衡

這裏是我的問題:
1)Pool是什麼時候創建了它的上下文,後來用於序列化?
只要在Container定義之後創建Pool對象,下面的示例就會正常運行。如果您交換池初始化,則會發生序列化錯誤。在我的生產代碼中,我想在定義容器類之前初始化Pool方式。是否可以刷新池「上下文」或以另一種方式實現此目的。
2)池是否有自己的負載均衡機制,如果有的話它是如何工作的?
如果我在我的i7機器上運行一個類似的例子,其中包含8個進程的池,我會得到以下結果:
- 對於輕評估函數池支持僅使用一個進程進行計算。它按要求創建了8個進程,但在大多數情況下只有一個被使用(我從內部打印了pid,並在htop中看到了這一點)。
- 對於繁重的評估功能,行爲與預期相同。它同樣使用所有8個流程。

3)當使用池時,我總是看到4個更多的進程,我要求(即池(進程= 2),我看到6個新進程)。他們的角色是什麼?

我使用Linux與Python 2.7.2

from multiprocessing import Pool 
from datetime import datetime 

POWER = 10 

def eval_power(container): 
    for power in xrange(2, POWER): 
     container.val **= power 
    return container 

#processes = Pool(processes=2) 

class Container(object): 
    def __init__(self, value): 
     self.val = value 

processes = Pool(processes=2) 

if __name__ == "__main__": 
    cont = [Container(foo) for foo in xrange(20)] 
    then = datetime.now() 
    processes.map(eval_power, cont) 
    now = datetime.now() 
    print "Eval time:", now - then 


編輯 - BAKURIU
1)我怕是這種情況。
2)我不明白什麼Linux調度程序必須做與python分配計算進程。我的情況可以通過下面的例子ilustrated:

from multiprocessing import Pool 
from os import getpid 
from collections import Counter 


def light_func(ind): 
    return getpid() 


def heavy_func(ind): 
    for foo in xrange(1000000): 
     ind += foo 
    return getpid() 


if __name__ == "__main__": 
    list_ = range(100) 
    pool = Pool(4) 
    l_func = pool.map(light_func, list_) 
    h_func = pool.map(heavy_func, list_) 

    print "light func:", Counter(l_func) 
    print "heavy func:", Counter(h_func) 


在我的酷睿i5的機器(4個線程)我得到以下結果:
光FUNC:計數器({2967:100})
重FUNC :計數器({2969:28,2967:28,2968:23,2970:21})

看來情況與我描述的一樣。但是我仍然不明白爲什麼python會這樣做。我的猜測是,它會盡量減少通信費用,但仍然不知道它用於負載平衡的機制。文檔也不是很有幫助,多處理模塊記錄很差。
3)如果我運行上面的代碼,我得到4個更多的過程,如前所述。屏幕來自HTOP:http://i.stack.imgur.com/PldmM.png

回答

2
  1. Pool對象調用__init__期間創建子進程,因此你必須以前定義Container。順便說一下,我不會將所有代碼包含在單個文件中,而是使用模塊來實現Container和其他實用程序,並編寫一個啓動主程序的小文件。

  2. Pool完全符合documentation中描述的內容。特別是它無法控制進程的調度,因此你看到的是Linux的調度程序認爲它是正確的。對於小型計算,它們花費的時間很少,調度程序不會打擾它們並行(這可能由於核心關聯等而具有更好的性能)。

  3. 您能用一個示例顯示此內容,以及您在任務管理器中看到的內容?我認爲他們可能是處理Pool內隊列的進程,但我不確定。在我的機器上,我只能看到主進程和兩個子進程。在點2


更新:

Pool對象只是把任務到一個隊列,子進程從隊列中獲取的參數。如果一個進程幾乎沒有時間執行一個對象,那麼Linux調度程序會讓該進程執行更多的時間(從而消耗更多的隊列中的項目)。如果執行花費很多時間,那麼這個調度器將改變進程,因此其他子進程也被執行。

在你的情況下,一個進程正在消耗所有的項目,因爲在其他子進程準備好之前它已經完成所有項目的計算時間很少。

正如我所說,Pool沒有做任何關於平衡子過程的工作。它只是一個隊列和一堆工作人員,池將項目放入隊列中,進程獲取項目並計算結果。 AFAIK它唯一能夠控制隊列的方法是將一定數量的任務放入隊列中的單個項目中(請參閱the documentation),但不能保證哪個進程將搶佔哪個任務。其他一切都留給了操作系統。

在我的機器上,結果不那麼極端。對於輕量計算,兩個進程的調用次數約爲另外兩次,而對於重量較大的進程,處理的項目或多或少具有相同的數量。可能在不同的操作系統和/或硬件上我們會得到不同的結果。

+0

我已在編輯部分回答了您的問題。 – Michal 2013-04-21 14:43:52

+0

@Michal我已經擴大了我對第2點的回答。關於第3點,我不知道。你是否試圖殺死其中一個子流程並看看會發生什麼?也許從你得到的錯誤中我們可以猜出它的用途(即使我的機器上沒有額外的進程,正如我所說的)。 – Bakuriu 2013-04-21 15:37:14