2016-05-31 103 views
12

我想幫助理解我做了什麼/爲什麼我的代碼沒有運行,因爲我期望。Python - 循環並行與joblib

我已經開始使用joblib通過並行運行(大)循環來嘗試加速我的代碼。

我使用它,像這樣:

from joblib import Parallel, delayed 
def frame(indeces, image_pad, m): 

    XY_Patches = np.float32(image_pad[indeces[0]:indeces[0]+m, indeces[1]:indeces[1]+m, indeces[2]]) 
    XZ_Patches = np.float32(image_pad[indeces[0]:indeces[0]+m, indeces[1],     indeces[2]:indeces[2]+m]) 
    YZ_Patches = np.float32(image_pad[indeces[0],     indeces[1]:indeces[1]+m, indeces[2]:indeces[2]+m]) 

    return XY_Patches, XZ_Patches, YZ_Patches 


def Patch_triplanar_para(image_path, patch_size): 

    Image, Label, indeces = Sampling(image_path) 

    n = (patch_size -1)/2 
    m = patch_size 

    image_pad = np.pad(Image, pad_width=n, mode='constant', constant_values = 0) 

    A = Parallel(n_jobs= 1)(delayed(frame)(i, image_pad, m) for i in indeces) 
    A = np.array(A) 
    Label = np.float32(Label.reshape(len(Label), 1)) 
    R, T, Y = np.hsplit(A, 3) 

    return R, T, Y, Label 

我一直在嘗試「n_jobs」,期待,增加這將加快我的功能。但是,隨着我增加n_jobs,事情變得非常緩慢。在沒有「並行」的情況下運行此代碼時,情況會比較慢,直到我將作業數量從1增加爲0.

爲什麼會出現這種情況?我明白我工作越多,腳本越快?我使用這個錯誤?

謝謝!

+0

首先,您在計算機上運行了多少個CPU或核心? 其次,'n_jobs'設置了同時運行的作業的最大數量。你嘗試過'n_jobs = -1'嗎?這應該使用您的計算機中的所有CPU。第三,這個for循環的「indeces」有多大? – fedepad

+0

我有24個內核和大量的內存。 indeces約有10,000個條目,所以認爲這將是一件好事情並行。我可以嘗試n_jobs = -1並回報。 – JB1

+0

是的。我可以想象,如果你將n_jobs從1增加到max(n_jobs = 23,njobs = -1),那麼你會達到一個點,在這個點上增加這個數字將涉及更多的開銷,所以你必須找到一個最佳點。當然,如果你可以使用後端=「線程」可能會更好,但你必須嘗試。 – fedepad

回答

3

也許你的問題是由於image_pad是一個大陣列而引起的。在您的代碼中,您正在使用joblib的默認multiprocessing後端。這個後端創建了一個工作者池,每個工作者都是一個Python進程。然後將該函數的輸入數據複製n_jobs次並廣播給池中的每個工作人員,這可能導致嚴重的開銷。從joblib的文檔引用:

默認情況下池的工人是真正的Python程序使用時n_jobs = 1作爲輸入傳遞到並行調用的參數是Python標準庫的多模塊叉式!序列化並在每個工作進程的內存中重新分配。

這對於大型參數可能會有問題,因爲它們將由工人重新分配n_jobs次。

由於在基於numpy的數據結構的科學計算中經常會發生這個問題,joblib.Parallel提供了一個特殊的處理方法,用於大型數組自動將它們轉儲到文件系統上並將引用傳遞給worker以打開它們作爲內存映射文件使用numpy.ndarray的numpy.memmap子類。這樣可以在所有工作進程之間共享一段數據。

注意:以下僅適用於默認的「多處理」後端。如果你的代碼可以釋放GIL,那麼使用後端=「線程化」更加高效。

因此,如果您遇到這種情況,你應該切換到後臺線程,如果你能打電話frame時釋放全局解釋鎖,或者切換到joblib共享內存的方式。

docsjoblib提供了一個自動的memmap轉換,可能是有用的。

2

這是很有可能的,你遇到的問題是python編譯器的本質的根本。

如果您閱讀「https://www.ibm.com/developerworks/community/blogs/jfp/entry/Python_Is_Not_C?lang=en」,可以從專業人士那裏瞭解到,專業人員專門優化和並行化python代碼,通過大循環進行迭代對於python線程來說是一項固有的緩慢操作。因此,產生循環數組的更多進程只會減慢速度。

但是 - 有些事情是可以做到的。

CythonNumba編譯器都旨在優化代碼類似於C/C++風格(即你的情況下) - 尤其是Numba新@vectorise裝飾允許標量函數採取並與大數組大型陣列應用操作以平行方式(target=Parallel)。

我不明白你的代碼足以給出一個實現的例子,但試試這個!這些編譯器以正確的方式使用,在過去並行進程中爲我帶來了3000,000%的速度提升!