簡體   English   中英

Python多處理:只有一個進程正在運行

[英]Python Multiprocessing: Only one process is running

我正在嘗試使用Python多處理模塊生成多個並行進程。 基本上,我做了類似的事情

pool = Pool(30)
results = [pool.apply_async(foo, (trainData, featureVector, terms, selLabel)) for selLabel in selLabels]
for r in results:
    tmp = r.get()
    modelFiles[tmp[0]] = tmp[1]

產生了30個進程,但是,似乎大多數進程已進入休眠狀態,而實際只有一個進程正在運行。 以下是我從ps得到的:

PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

31267 74.6  2.4 7125412 6360080 pts/1 Sl+  13:06  24:25  \_ python2.6 /home/PerlModules/Python/DoOVA.py

31427 27.4  2.3 6528532 6120904 pts/1 R+   13:20   5:18      \_ python2.6 /home/PerlModules/Python/DoOVA.py

31428  0.0  1.3 4024724 3617016 pts/1 S+   13:20   0:00      \_ python2.6 /home/PerlModules/Python/DoOVA.py

31429  0.0  1.3 4024724 3617016 pts/1 S+   13:20   0:00      \_ python2.6 /home/PerlModules/Python/DoOVA.py

31430  0.0  1.3 4024724 3617016 pts/1 S+   13:20   0:00      \_ python2.6 /home/PerlModules/Python/DoOVA.py

DoOVA.py是我正在運行的腳本。 他們中的大多數人的身份都是S+

誰能給我一些關於問題的線索? 我知道輸入爭論featureVector大小非常大,比如大約300MB。 那會是個問題嗎? 我運行的機器有幾TB的內存。

foo做的事情如下:

def foo(trainData, featureVector, terms, selLabel, penalty):
    outputFile = 'train_'+selLabel+'.dat'
    annotation = dict()
    for id in trainData:
        if trainData[id] == selLabel:
            annotation[id] = '1'
        else:
            annotation[id] = '-1'
    try:
        os.mkdir(selLabel)
        os.chdir(selLabel)
    except OSError:
        os.chdir(selLabel)
    ###Some more functions, which involves a command line call through call from subprocess module
    os.chdir('../')
    return (selLabel, 'SVM_' + selLabel + '.model')

所有其他輸入參數的大小都很小。 機器至少有100 cpus。 在每次運行中,即使在創建任何目錄之前,腳本也需要很長時間,盡管在os.mkdir()之前foo中沒有發生重大計算。

正如評論指出要通過featureVector使用initializerinitargs參數Pool 在Unix類型的系統上,這將導致大量的性能提升(即使selLabels只有1個項目),因為該值將使用os.fork基本上免費傳遞給子進程。 否則,每次調用foofeatureVector都將被父進程pickle,通過管道傳遞並由子進程進行unpickled。 這將花費很長時間,並且基本上將序列化所有子進程,因為它們將等待父進程腌制並featureVector發送每個調用的featureVector副本。

由於對於我上面談論的內容存在一些困惑,所以這里有一個更長的解釋,說明代碼中發生的內容與當前編寫的內容有關:

創建Pool對象時,將立即創建30個工作進程,主進程的所有子進程都創建了Pool對象。 為了與每個子進程進行通信,創建了一個管道。 此管道允許父進程和子進程之間的雙向通信。 父級使用管道來指示子進程執行的操作,子級使用管道通知父級任何操作的結果。

當您第一次調用pool.apply_async ,父進程通過管道發送命令,指示子進程使用提供的參數執行foo函數。 由於其中一個論點是巨大的,300MB,這最終需要很長時間。 父進程必須pickle對象。 這會將對象(及其引用的所有內容)轉換為可通過管道發送的字節流。

由於管道只能容納大約64k(Linux默認值),並且您發送的內容遠不止這些,因此可以有效地同步父進程和其中一個子進程。 父進程只能以子進程可以接收和取消它們的速度發送參數,並且子進程只能像父進程一樣快地接收參數並發送它們。 雖然這是在進行所有其他子進程必須等待。 父進程一次只能向一個子進程發送命令。

一旦父進程完成了第一次調用foo所有參數的發送,它就可以繼續發送命令再次調用foo 在此之后不久,一旦子進程收到所有參數,孩子就會調用foo (這就是為什么在創建任何目錄之前需要很長時間,甚至在foo被調用之前需要很長時間。)在foo返回之后,子進程將等待父進程發送另一個命令。 如果foo本身需要足夠短的時間來執行,那么接收第一個命令來調用foo同一子進程也可能會收到第二個調用foo命令。

除非foo本身需要很長時間才能執行,只要比通過管道發送featureVector花費的時間長或長,那么你將被有效地限制為只執行一個子進程。 父進程將嘗試命令子進程盡可能快地調用foo ,但由於featureVector太大,它只能以非常慢的速率執行。 一旦完成將命令發送到一個進程來調用foo ,它命令調用foo的前一個進程很久以前就已經完成了對foo調用。 運行子進程之間幾乎沒有重疊。

為了解決代碼中的性能問題,您需要執行以下操作:

def child_initialize(_trainData, _featureVector, _terms):
     global trainData, featureVector, terms
     trainData = _trainData
     featureVector = _featureVector
     terms = _terms

def foo(selLabel):
     ...

pool = Pool(30, initialize = child_initialize, initargs = (trainData, featureVector, terms))
results = [pool.apply_async(foo, (selLabel,)) for selLabel in selLabels]

此代碼還使用initargs傳遞trainDataterm ,假設它們也不會更改。

雖然這應該會帶來巨大的性能提升,並允許子進程並行運行,但這並不一定意味着子進程將以更常見的狀態出現在可運行狀態的ps中。 您的示例foo函數看起來似乎將花費大部分時間等待“命令行調用”完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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