2012-03-30 99 views
5

我正在matlab中使用長時間運行的parfor循環。Matlab並行計算工具箱,在parfor循環中動態分配工作

parfor iter=1:1000 
    chunk_of_work(iter); 
end 

每次運行通常大約有2-3次異常值。也就是說,每完成1000塊工作,就有2-3個工作比其他工作時間長100倍。隨着環路接近完成,評估異常值的工作人員繼續運行,而其餘工人沒有計算負擔。

這與靜態分配工作的parfor循環一致。這是與並行計算工具箱found here文檔對比:

「工作分配是動態的,而不是被分配一個固定的 迭代範圍內,工人被分配一個新的迭代只 後,他們處理完自己的當前的迭代,這導致了即使工作負載分配也是如此。「

有關發生了什麼的任何想法?

回答

5

我認爲你引用的doc有一個很好的描述什麼被認爲是工作的靜態分配:每個工人「被分配一個固定的迭代範圍」。對於4名工作人員來說,這意味着首先被分配爲iter 1:250,第二個iter 251:500,...或第一個爲1:4:100,第二個爲2:4:1000,等等。

你沒有確切地說出你觀察到的情況,但是你所描述的與動態工作負載分配很好地一致:首先,四個(示例)工作者分別工作在一個iter上,第一個完成了第五個工作,下一個完成(如果前四個中的三個需要更長的時間,這可能是相同的)在第六個等等上工作。現在如果你的離羣值是按照MATLAB選擇處理循環迭代的次數爲20,850和900,並且每次需要100次,這隻意味着第四次到第三十次迭代將由四個工人中的三個來解決,忙於第20次(到320會完成,現在假設非離羣計算時間大致均勻分佈)。然而,被分配了第850次迭代的工作者將繼續運行,即使在另一個已經解決了#1000,並且對於#900也是如此。實際上,如果有大約1100次迭代,那麼在#900上工作的那個應該大致在其他時間完成。

[編輯爲原單的措辭暗示MATLAB仍然會分配PARFOR循環迭代,以從1到1000,這不應該被假定]

所以長話短說,除非你想辦法首先處理你的異常值(當然這需要你先知道哪些是異常值,並找到一種方法讓MATLAB用這些異常值開始parfor循環處理),但動態工作負載分佈本身無法避免你觀察到的效果。

增加:我想,但是,你的觀察,作爲「環接近完成時,工人* 小號 *該評估的異常值繼續運行」,似乎暗示以下

    的至少一個
  1. 異常值在某種程度上是最後迭代MATLAB開始處理
  2. 你有很多的工人當中,在迭代次數的數量級
  3. 你離羣值(2-3)的估計數或估他們的計算時間罰分(因子100)太低
3

PARFOR中的工作分佈有些確定性。您可以通過讓每個工作人員登錄到磁盤來準確觀察發生了什麼事情,但基本上,事實證明,PARFOR將循環以確定性方式分成塊,但動態地將它們排除在外。不幸的是,目前還沒有辦法控制這種組塊。

但是,如果您無法預測您的1000個案例中哪些是異常值,那麼很難想象分配工作的有效方案。

如果你可以預測你的離羣值,你可能會利用這樣的事實:大致來說,PARFOR以相反的順序執行循環迭代,所以你可以把它們放在循環的「結尾」,這樣工作就開始了他們立即。

3

你面對的問題在@ arne.b的答案中有很好的描述,我沒有什麼要補充的。

但是,並行計算工具箱確實包含用於分解job into tasks以獨立執行的函數。從你的問題,不可能得出這樣的結論:這是合適的,或者這不適合你的應用。如果是這樣,總的策略是把工作分解成一些規模的任務,並讓每個處理器完成一項任務,當完成回到未完成的任務堆棧並開始另一個任務時。

您可能可以分解您的問題,使得一個任務替換一個循環迭代(大量任務,管理計算但負載平衡最佳)或者讓一個任務替換N個循環迭代(更少的任務,開銷較少,負載平衡較差)。作業和任務也比parfor執行起來有點棘手。

0

作爲PARFOR的替代方案,在R2013b及更高版本中,您可以使用PARFEVAL並按照您認爲合適的方式分割作品。一旦你有足夠的結果,你甚至可以取消'時間異常值',如果這是適當的。當然,將現有環路劃分爲1000個單獨的遠程PARFEVAL呼叫時會產生開銷。也許這是一個問題,也許不是。這是我想象的那種東西:

for idx = 1:1000 
    futures(idx) = parfeval(@chunk_of_work, 1, idx); 
end 
done = false; numComplete = 0; 
timer = tic(); 
while ~done 
    [idx, result] = fetchNext(futures, 10); % wait up to 10 seconds 
    if ~isempty(idx) 
     numComplete = numComplete + 1; 
     % stash result 
    end 
    done = (numComplete == 1000) || (toc(timer) > 100); 
end 
% cancel outstanding work, has no effect on completed futures 
cancel(futures);