2010-02-13 17 views
12

मैं डी प्रोग्रामिंग भाषा के लिए समांतरता पुस्तकालय पर काम कर रहा हूं। अब जब मैं बुनियादी प्राइमेटिव (समांतर फोरैच, मानचित्र, कम और कार्य/वायदा) से बहुत खुश हूं, तो मैं कुछ उच्च स्तर के समांतर एल्गोरिदम के बारे में सोचना शुरू कर रहा हूं। समांतरता के लिए अधिक स्पष्ट उम्मीदवारों में से एक है।(कब) समानांतर प्रकार के व्यावहारिक हैं और आप एक कुशल कैसे लिखते हैं?

मेरा पहला सवाल है, वास्तविक दुनिया में उपयोगी एल्गोरिदम को क्रमबद्ध करने के समानांतर संस्करण हैं, या वे ज्यादातर अकादमिक हैं? अगर वे उपयोगी हैं, तो वे कहाँ उपयोगी हैं? मैं व्यक्तिगत रूप से शायद ही कभी अपने काम में उनका उपयोग करता हूं, क्योंकि मैं आमतौर पर एक ही प्रकार() कॉल की तुलना में समांतरता के एक बहुत अधिक समेकित स्तर का उपयोग करके 100% पर अपने सभी कोरों को पेग करता हूं।

दूसरा, ऐसा लगता है कि त्वरित तरह से बड़े सरणी के लिए लगभग शर्मनाक समानांतर है, फिर भी मुझे लगता है कि मुझे लगता है कि मुझे मिलना चाहिए कि मुझे निकट-रैखिक गति मिल सकती है। एक त्वरित क्रम के लिए, केवल अंतर्निहित धारावाहिक भाग पहला विभाजन है। मैंने प्रत्येक विभाजन के बाद, समानांतर में दो subarrays को क्रमबद्ध करके, एक त्वरित क्रमांतर समानांतर करने की कोशिश की। सरलीकृत स्यूडोकोड में:

// I tweaked this number a bunch. Anything smaller than this and the 
// overhead is smaller than the parallelization gains. 
const smallestToParallelize = 500; 

void quickSort(T)(T[] array) { 
    if(array.length < someConstant) { 
     insertionSort(array); 
     return; 
    } 

    size_t pivotPosition = partition(array); 

    if(array.length >= smallestToParallelize) { 
     // Sort left subarray in a task pool thread. 
     auto myTask = taskPool.execute(quickSort(array[0..pivotPosition])); 
     quickSort(array[pivotPosition + 1..$]); 
     myTask.workWait(); 
    } else { 
     // Regular serial quick sort. 
     quickSort(array[0..pivotPosition]); 
     quickSort(array[pivotPosition + 1..$]); 
    } 
} 

यहां तक ​​कि बहुत बड़ी सरणियाँ, जहां समय पहले विभाजन लेता नगण्य है के लिए, मैं यह कर सकते हैं केवल एल्गोरिथ्म के एक विशुद्ध रूप से धारावाहिक संस्करण की तुलना में एक दोहरे कोर पर एक 30% speedup, के बारे में मिलता है । मैं अनुमान लगा रहा हूं कि बाधा उत्पन्न स्मृति मेमोरी एक्सेस है। इस बाधा को खत्म करने के बारे में कोई अंतर्दृष्टि या बाधा क्या हो सकती है?

संपादित करें: मेरे कार्य पूल में सिस्टम की कम से कम कोर की संख्या के बराबर थ्रेड की निश्चित संख्या है (मुख्य धागा भी काम करता है)। साथ ही, जिस प्रकार का इंतजार मैं उपयोग कर रहा हूं वह एक काम प्रतीक्षा है, यानी यदि कार्य शुरू हो गया है लेकिन समाप्त नहीं हुआ है, तो workWait() कॉल करने वाले थ्रेड पूल से अन्य नौकरियों को चुरा लेता है और जब तक वह इंतजार कर रहा है तब तक ऐसा करता है। यदि कार्य शुरू नहीं हुआ है, तो यह वर्तमान धागे में पूरा हो गया है। इसका मतलब है कि प्रतीक्षा अक्षम नहीं है। जब तक काम करने के लिए काम किया जाता है, तब तक सभी धागे व्यस्त रहेंगे।

+0

मैं नहीं जानता कि कैसे अपने quicksort बेहतर parallelize बनाने के लिए, लेकिन वहाँ एक संस्करण samplesort कहा जाता है जो मूल रूप से एक वेनिला quicksort की तुलना में बहुत तेजी से होता है, और है के रूप में जहाँ तक मैं देख सकते हैं, यह समानांतर समान होना चाहिए। –

उत्तर

7

ध्यान रखें मैं समानांतर प्रकार पर एक विशेषज्ञ नहीं हूँ, और लोगों को अनुसंधान करियर समानांतर तरह से बाहर है, लेकिन ...

1) वे असली दुनिया में उपयोगी होते हैं।

बेशक, यदि आपको कुछ महंगा (जैसे स्ट्रिंग्स या बदतर) सॉर्ट करने की आवश्यकता है, तो आप सभी कोरों को छेड़छाड़ नहीं कर रहे हैं।

  • लगता यूआई कोड जहां संदर्भ के आधार पर तार की एक बड़ी गतिशील सूची सॉर्ट करने के लिए की जरूरत है
  • लगता है कि एक बार्न्स-झोपड़ी एन शव सिम जहां कणों सॉर्ट करने के लिए की जरूरत है की तरह कुछ

2) क्विक्सोर्ट ऐसा लगता है जैसे यह रैखिक गति प्रदान करेगा, लेकिन ऐसा नहीं है। विभाजन चरण अनुक्रमिक बाधा है, यदि आप प्रोफ़ाइल करते हैं तो आप इसे देखेंगे और यह क्वाड कोर पर 2-3x पर कैप आउट करेगा।

यदि आप एक छोटी प्रणाली पर अच्छी गति प्राप्त करना चाहते हैं तो आपको यह सुनिश्चित करने की ज़रूरत है कि आपके प्रति कार्य ओवरहेड वास्तव में छोटे हैं और आदर्श रूप से आप यह सुनिश्चित करना चाहते हैं कि आपके पास बहुत सारे धागे चल रहे हों, यानी नहीं दोहरी कोर पर 2। एक थ्रेड पूल शायद सही अमूर्त नहीं है।

यदि आप एक बड़े सिस्टम पर अच्छी गति प्राप्त करना चाहते हैं तो आपको स्कैन आधारित समांतर प्रकारों को देखने की आवश्यकता होगी, इस पर कागजात हैं। बिगोनिक सॉर्ट भी मर्ज सॉर्ट के रूप में काफी आसान समानांतर है। समानांतर रेडिक्स सॉर्ट भी उपयोगी हो सकता है, पीपीएल में एक है (यदि आप विजुअल स्टूडियो 11 के विपरीत नहीं हैं)।

3

मैं कोई विशेषज्ञ हूँ लेकिन ... यहाँ मैं क्या देखने के लिए चाहते हैं पर है: कि एक सामान्य नियम के, एल्गोरिदम कि के छोटे अंश को देखने के रूप में

सबसे पहले, मैंने सुना है शुरुआत से एक समस्या समानांतर एल्गोरिदम के रूप में बेहतर काम करता है।

अपने कार्यान्वयन को देखते हुए, बनाने समानांतर/धारावाहिक स्विच अन्य रास्ता तय की कोशिश: सरणी और तरह समानांतर में विभाजन जब तक आप एन क्षेत्रों है, तो धारावाहिक जाना। यदि आप प्रत्येक समानांतर मामले के लिए एक नए धागे को कम या कम पकड़ रहे हैं, तो एन ~ आपकी कोर गिनती होनी चाहिए। ओटीओएच अगर आपका थ्रेड पूल निश्चित आकार का है और अल्पकालिक प्रतिनिधियों की कतार के रूप में कार्य करता है, तो मैं आपकी कोर गिनती के एन ~ 2+ बार उपयोग करता हूं (ताकि कोर निष्क्रिय न हों क्योंकि एक विभाजन तेजी से समाप्त हो गया हो)।

अन्य तोड़ मरोड़:

  • स्थानीय स्तर पर myTask.wait(); छोड़ सकते हैं और नहीं बल्कि एक आवरण समारोह है कि सभी कार्य पर इंतजार कर रहा है है।
  • गहराई की जांच से बचने वाले फ़ंक्शन का एक अलग धारावाहिक कार्यान्वयन करें।
+0

+1। अच्छा स्पष्टीकरण .. – bragboy

1

"मेरा पहला सवाल है, असली दुनिया में उपयोगी एल्गोरिदम को क्रमबद्ध करने के समानांतर संस्करण हैं" - वास्तविक कार्य में आप जिस डेटा सेट पर काम कर रहे हैं उसके आकार पर निर्भर करता है। डेटा के छोटे सेट के लिए उत्तर नहीं है। बड़े डेटा सेट के लिए यह न केवल डेटा सेट के आकार पर निर्भर करता है बल्कि सिस्टम के विशिष्ट आर्किटेक्चर पर निर्भर करता है।

सीमित कारकों में से एक जो प्रदर्शन में अपेक्षित वृद्धि को रोक देगा सिस्टम का कैश लेआउट है। यदि डेटा कोर के एल 1 कैश में फिट हो सकता है, तो सॉर्टिंग एल्गोरिदम के प्रत्येक पुनरावृत्ति के बीच एल 1 कैश मिस के दंड के रूप में कई कोरों में सॉर्ट करके लाभ प्राप्त होता है।

एक ही तर्क चिप्स कई एल 2 कैश और NUMA (गैर वर्दी स्मृति पहुँच) आर्किटेक्चर है पर लागू होता है। इसलिए जितना अधिक कोर आप सॉर्टिंग को वितरित करना चाहते हैं, सबसे छोटा टॉपरलेलाइज़ स्थिर निरंतर बढ़ाना होगा।

आपके द्वारा पहचाना जाने वाला एक और सीमित कारक स्मृति मेमोरी एक्सेस या स्मृति बस पर विवाद साझा किया जाता है। चूंकि मेमोरी बस केवल प्रति सेकंड मेमोरी एक्सेस की एक निश्चित संख्या को संतुष्ट कर सकती है; अतिरिक्त कोर जो अनिवार्य रूप से कुछ भी नहीं करते हैं लेकिन मुख्य स्मृति को पढ़ते हैं और लिखते हैं, वे स्मृति प्रणाली पर बहुत अधिक तनाव डालते हैं।

पिछले पहलू यह है कि मैं कहना चाहिए थ्रेड पूल खुद के रूप में यह रूप में आपको लगता है के रूप में कुशल नहीं हो सकता है। चूंकि आपके पास धागे हैं जो एक साझा कतार से काम चोरी और उत्पन्न करते हैं, उस कतार को सिंक्रनाइज़ेशन विधियों की आवश्यकता होती है; और यह लागू करने के तरीके के आधार पर, वे आपके कोड में बहुत लंबे सीरियल अनुभाग का कारण बन सकते हैं।

1

मैं अगर जवाब यहां किसी भी अब लागू होते हैं पता नहीं है या अगर मेरे सुझाव डी

वैसे भी ...

यह मानते हुए कि डी यह अनुमति देता है पर लागू होते हैं, वहाँ हमेशा उपलब्ध कराने की संभावना है prefetch कैश के लिए संकेत देता है। प्रश्न में मूल अनुरोध करता है कि डेटा जल्द ही (तुरंत नहीं) को एक निश्चित कैश स्तर में लोड किया जाना चाहिए। आदर्श मामले में जब डेटा उस पर काम करना शुरू कर देता है तब तक डेटा प्राप्त किया जाएगा। अधिक संभावना है कि prefetch प्रक्रिया कम से कम कम राज्यों की तुलना में कम या कम परिणाम होगा, अगर डेटा "ठंडा" लाया गया था।"

आप अभी भी समग्र कैश-टू-रैम थ्रूपुट क्षमता से बाधित होंगे, इसलिए आपको डेटा व्यवस्थित करना होगा कि कोर के अनन्य कैश में इतना डेटा है कि यह उचित मात्रा में खर्च कर सकता है अद्यतन डेटा लिखने से पहले समय।

कैश लाइनों (प्रत्येक 64 बाइट्स की इकाइयों को लाने) की अवधारणा के अनुसार कोड और डेटा को व्यवस्थित करने की आवश्यकता है जो कैश में सबसे छोटी आकार की इकाई है। उसमें दो कोरों के लिए काम को व्यवस्थित करने की आवश्यकता है ताकि मेमोरी सिस्टम आधे से ज्यादा कोर (100% स्केलेबिलिटी मान ले) जैसा कि पहले एक कोर काम कर रहा था और काम नहीं किया गया था। चार कोर एक चौथाई के लिए उतना ही अधिक। यह काफी चुनौती है लेकिन किसी भी तरह से असंभव नहीं, यह सिर्फ वंचित है काम पर पुनर्गठन में आप कितने कल्पनाशील हैं। हमेशा की तरह, ऐसे समाधान होते हैं जिन्हें कल्पना नहीं की जा सकती ... जब तक कि कोई ऐसा नहीं करता!

मुझे नहीं पता कि WYSIWYG डी की तुलना सी से की जाती है - जिसका मैं उपयोग करता हूं - लेकिन आम तौर पर मुझे लगता है कि स्केल करने योग्य अनुप्रयोगों को विकसित करने की प्रक्रिया को वास्तविक मशीन कोड पीढ़ी में कंपाइलर को कितना प्रभावित कर सकता है। व्याख्या की गई भाषाओं के लिए दुभाषिया द्वारा इतनी मेमोरी काम चल रहा है कि आप सामान्य "पृष्ठभूमि शोर" से सुधार को समझने में सक्षम नहीं हैं।

मैंने एक बार एक बहु थ्रेडेड शेलसॉर्ट लिखा था जो एक से तुलना में तीन कोरों पर एक और 100% की तुलना में दो कोरों पर 70% तेज था। चार कोर तीन से धीमी गति से भाग गया। तो मैं आपको बताई गई दुविधाओं को जानता हूं।

0

मैं आपको बाहरी सॉर्टिंग [1] पर इंगित करना चाहता हूं जो समान समस्याओं का सामना करता है। आम तौर पर, एल्गोरिदम का यह वर्ग ज्यादातर डेटा के बड़े खंडों का सामना करने के लिए उपयोग किया जाता है, लेकिन उनका मुख्य बिंदु यह है कि वे बड़े हिस्से को छोटी और असंबंधित समस्याओं में विभाजित करते हैं, जो समानांतर में दौड़ने के लिए वास्तव में बहुत अच्छे हैं। आपको "केवल" आंशिक परिणामों को बाद में सिलाई करने की आवश्यकता है, जो समानांतर नहीं है (लेकिन वास्तविक सॉर्टिंग की अपेक्षा अपेक्षाकृत सस्ते)।

एक बाहरी मर्ज सॉर्ट भी अज्ञात मात्रा में धागे के साथ काम करेगा। आप केवल काम-भार को मनमाने ढंग से विभाजित करते हैं, और जब भी कोई निष्क्रिय होता है, तब तक एन तत्वों के प्रत्येक खंड को धागे में देते हैं, जब तक कि आपकी सभी कार्य इकाइयां न हो जाएं, उस बिंदु पर आप उन्हें शामिल करना शुरू कर सकते हैं।

[1] http://en.wikipedia.org/wiki/External_sorting

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