2010-07-19 42 views
12

我需要並行化一個方法,對列表中的元素進行詳盡的成對比較。串行實現很簡單:嵌套的Parallel.ForEach循環在同一個列表中?

foreach (var element1 in list) 
    foreach (var element2 in list) 
     foo(element1, element2); 

在這種情況下,foo不會改變元素1或元素2的狀態。我知道這是不是安全,簡單地做嵌套Parallel.ForEach聲明:

Parallel.ForEach(list, delegate(A element1) 
{ 
    Parallel.ForEach(list, delegate(A element2) 
    { 
     foo(element1, element2); 
    }); 
}); 

會是什麼來實現這個使用並行任務庫的理想方式?

回答

11

難道你不能只有一個並行和一個正常循環?因此,無論

Parallel.ForEach(list, delegate(A element1) 
{ 
    foreach(A element2 in list) 
    foo(element1, element2) 
});

foreach(A element1 in list) 
{ 
    Parallel.ForEach(list, delegate(A element2) 
    { 
    foo(element1, element2); 
    }); 
}

應該加快步伐爲好。無論如何,每個循環都永遠不會有線程,所以這可能會比嵌套並行循環更快或更慢。

14

至少如果你在一臺機器,其中核心的數量至少兩次在列表項目的數量上執行的代碼,我不知道它是做嵌入式Parallel.ForEach個好主意。

換句話說,如果您的目標是四核,並且列表中有一千個項目,則只需對父循環進行並行化。並行化兩個循環並不會使代碼更快,但是由於並行任務具有性能成本,所以它們要慢得多,速度要慢得多。

alt text http://www.freeimagehosting.net/uploads/ca97f403f8.png

在每次迭代中,幾毫秒內將被Parallel.ForEach丟失,以確定哪個線程必須執行下一次迭代。假設你有一組7個項目。如果您並行化父環路,那麼這些毫秒將丟失7次。如果你並行兩個循環,它們將會丟失7×7 = 49次。設置越大,過熱越大。

+3

不要以爲PFX將創造儘可能多的線程因爲有平行的任務 - 比這更聰明。 – 2010-07-19 13:58:02

+0

當然不是。默認情況下,它創建與核心一樣多的線程。但問題是,在每次迭代之後,它都會花時間試圖找出哪個線程必須執行下一次迭代。 – 2010-07-19 14:26:19

+0

我不認爲他會說有很多線程,只是爲每個函數調用排隊一個任務會比爲每個外部循環調用PFX引擎的開銷要多得多。 – Gabe 2010-07-19 14:28:22

1

這兩個嵌套循環基本上意味着你想foo該列表與自身的cartessian產品。通過首先在臨時列表中創建所有對,然後使用Parallel.ForEach遍歷該列表,可以並行化整個操作。

編輯:您可以使用迭代器來返回一個2元素的元組和組合,而不是創建所有組合的列表。 Parallel.ForEach仍然會並行處理元組。

下面的示例打印出當前迭代步驟表明,結果回來了序,如將並行處理過程中預計:

const int SIZE = 10; 
    static void Main(string[] args) 
    { 
     List<int> list = new List<int>(SIZE); 
     for(int i=0;i<SIZE;i++) 
     { 
      list.Add(i); 
     } 


     Parallel.ForEach(GetCombinations(list),(t,state,l)=> 
      Console.WriteLine("{0},{1},{2}",l,t.Item1,t.Item2)); 

    } 

    static IEnumerable<Tuple<int,int>> GetCombinations(List<int> list) 
    { 
     for(int i=0;i<list.Count;i++) 
      for(int j=0;j<list.Count;j++) 
       yield return Tuple.Create(list[i],list[j]); 
    } 
+0

你可能不知道Enumerable.Range()。這真的很方便!在代碼中,你在這裏運行了3個循環(而不是2個),其中2個並不完全平行。它取決於'Foo()'的作用(在你的答案中是Console.WriteLine),但是這可能會比不添加任何並行性和循環正常循環慢。 – 2011-07-05 21:22:03