2010-07-19 6 views
12

मुझे ऐसी विधि को समानांतर करने की आवश्यकता है जो सूची में तत्वों पर एक विस्तृत जोड़ी की तुलना करता है। धारावाहिक कार्यान्वयन सीधे आगे है:नेस्टेड समांतर। फोरएच लूप्स एक ही सूची में?

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

इस मामले में, foo element1 या element2 की स्थिति को नहीं बदलेगा। मैं जानता हूँ कि यह नेस्टेड 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 मान के रूप में कई धागे पैदा करेगा न करें क्योंकि समानांतर कार्य हैं - यह उससे बेहतर है। –

+0

बेशक नहीं। डिफ़ॉल्ट रूप से, यह कोर के रूप में कई धागे बनाता है। लेकिन समस्या यह है कि प्रत्येक पुनरावृत्ति के बाद, यह पता लगाने का प्रयास करेगा कि कौन सा धागा अगले पुनरावृत्ति को निष्पादित करेगा। –

+0

मुझे नहीं लगता कि वह कह रहा है कि कई धागे होने जा रहे हैं, बस प्रत्येक फंक्शन कॉल के लिए एक कार्य को एनक्यूइंग करने के लिए प्रत्येक बाहरी लूप के लिए पीएफएक्स इंजन का आह्वान करने की तुलना में अधिक ओवरहेड होगा। – Gabe

1

दो नेस्टेड लूपों का अनिवार्य रूप से मतलब है कि आप foo स्वयं के साथ सूची का कार्टेशियन उत्पाद चाहते हैं। आप सभी जोड़ों को अस्थायी सूची में पहले बनाकर पूरे ऑपरेशन को समानांतर कर सकते हैं, फिर उस सूची को समानांतर के साथ पुन: सक्रिय कर सकते हैं।

EDIT: सभी संयोजनों की सूची बनाने के बजाय, आप संयोजन के साथ 2-तत्व ट्यूपल को वापस करने के लिए एक पुनरावर्तक का उपयोग कर सकते हैं। समानांतर। फॉरएच अभी भी tuples की प्रसंस्करण समानांतर होगा।

बाहर वर्तमान यात्रा कदम नमूना प्रिंट निम्न पता चलता है कि परिणामों को वापस बाहर के आदेश आते हैं, के रूप में समानांतर प्रसंस्करण के दौरान उम्मीद होगी:

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), लेकिन यह संभवतः किसी समानांतरता को जोड़ने और सामान्य रूप से दो बार लूपिंग करने से धीमा होने वाला है। –

संबंधित मुद्दे