2011-12-08 14 views
7

पर सी में बहु-थ्रेडिंग की प्रोफाइलिंग पर विचारों की आवश्यकता है मेरा एप्लिकेशन परिदृश्य इस तरह है: मैं प्रदर्शन लाभ का मूल्यांकन करना चाहता हूं जो डेटा की मात्रा को संसाधित करने के लिए क्वाड-कोर मशीन पर प्राप्त कर सकता है। मेरे पास निम्नलिखित दो कॉन्फ़िगरेशन हैं:लिनक्स

i) 1-प्रक्रिया: 1 एम .. 1 जी से किसी भी थ्रेडिंग और प्रक्रिया डेटा के बिना एक प्रोग्राम, जबकि सिस्टम को अपने 4-कोर के केवल एक कोर को चलाने के लिए माना जाता था।

ii) 4-धागे-प्रक्रिया: 4-थ्रेड्स वाला एक प्रोग्राम (एक ही ऑपरेशन करने वाले सभी थ्रेड) लेकिन इनपुट डेटा का 25% प्रोसेसिंग।

4-थ्रेड बनाने के लिए मेरे प्रोग्राम में, मैंने pthread के डिफ़ॉल्ट विकल्प (यानी, किसी विशिष्ट pthread_attr_t के बिना) का उपयोग किया। मेरा मानना ​​है कि 1-प्रोसेस कॉन्फ़िगरेशन की तुलना में 4-थ्रेड कॉन्फ़िगरेशन का प्रदर्शन लाभ 400% (या कहीं 350% और 400% के बीच) होना चाहिए।

timer_start(&threadCreationTimer); 
pthread_create(&thread0, NULL, fun0, NULL); 
pthread_create(&thread1, NULL, fun1, NULL); 
pthread_create(&thread2, NULL, fun2, NULL); 
pthread_create(&thread3, NULL, fun3, NULL); 
threadCreationTime = timer_stop(&threadCreationTimer); 

pthread_join(&thread0, NULL); 
pthread_join(&thread1, NULL); 
pthread_join(&thread2, NULL); 
pthread_join(&thread3, NULL);  

इनपुट डेटा का आकार भी प्रत्येक थ्रेड की स्मृति आवश्यकता में वृद्धि हो सकती है में वृद्धि के बाद से, तो सभी डेटा लोड हो रहा:

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

timer_start(&threadTimer[i]); 
while(!dataFinished[i]) 
{ 
    threadTime[i] += timer_stop(&threadTimer[i]); 
    data_source(); 
    timer_start(&threadTimer[i]); 
    process(); 
} 
threadTime[i] += timer_stop(&threadTimer[i]); 

चर dataFinished[i] प्रक्रिया द्वारा true चिह्नित है जब यह प्राप्त है और इस प्रक्रिया में सभी आवश्यक डेटा।

execTime4Thread = max(threadTime[0], threadTime[1], threadTime[2], threadTime[3]) + threadCreationTime: Process() जानता है जब कि :-) क्या करना

मुख्य कार्य में, मैं समय नीचे के रूप में 4-पिरोया विन्यास द्वारा उठाए गए की गणना कर रहा हूँ।

और प्रदर्शन लाभ बस

gain = execTime1process/execTime4Thread * 100

जारी करके की जाती है: 4M करने के लिए 1M भर में छोटे डेटा आकार पर , प्रदर्शन लाभ आम तौर पर अच्छा है (350% से 400% के बीच)। हालांकि, प्रदर्शन लाभ की प्रवृत्ति इनपुट आकार में वृद्धि के साथ तेजी से घट रही है। यह 50 एम या उससे अधिक तक के कुछ डेटा आकार तक घटता रहता है, और फिर 200% के आसपास स्थिर हो जाता है। एक बार यह उस बिंदु तक पहुंचने के बाद, यह 1 जीबी डेटा के लिए लगभग स्थिर रहता है।

मेरा प्रश्न यह है कि कोई भी इस व्यवहार का मुख्य तर्क सुझा सकता है (यानी, शुरुआत में प्रदर्शन ड्रॉप और बाद में स्थिर रहें)?

और सुझावों को कैसे ठीक किया जाए?

आपकी जानकारी के लिए, मैंने प्रत्येक थ्रेड के लिए क्या हो रहा है यह देखने के लिए threadCreationTime और threadTime के व्यवहार की भी जांच की। डेटा के 1 एम के लिए इन चर के मान छोटे होते हैं और डेटा आकार में वृद्धि के साथ इन दोनों चर दोनों तेजी से बढ़ते हैं (लेकिन threadCreationTime डेटा आकार और threadTime पर ध्यान दिए बिना डेटा के संसाधित होने की दर से बढ़ना चाहिए)।50 एम तक बढ़ने के बाद या threadCreationTime स्थिर हो जाता है और threadTime (जैसे प्रदर्शन ड्रॉप स्थिर हो जाता है) और threadCreationTime संसाधित होने के लिए डेटा में वृद्धि के अनुरूप निरंतर दर पर बढ़ते रहते हैं (जिसे समझा जा सकता है)।

क्या आपको लगता है कि प्रत्येक थ्रेड के स्टैक आकार को बढ़ाने, प्रक्रिया प्राथमिकता सामग्री या अन्य पैरामीटर के कस्टम मान शेड्यूलर के प्रकार (pthread_attr_init का उपयोग करके) मदद कर सकते हैं?

पीएस: रूट के साथ लिनक्स के असफल सुरक्षित मोड के तहत प्रोग्राम चलाने के दौरान परिणाम प्राप्त किए जाते हैं (यानी, न्यूनतम ओएस जीयूआई और नेटवर्किंग सामान के बिना चल रहा है)।

+0

आपके सीपीयू का मॉडल क्या है? – Tudor

+4

धागे के बीच कैश का सबसे अधिक संभावना पार प्रदूषण। क्या आपने डेटा के हिस्सों के आकार को बदलने की कोशिश की है? आपको अपने माप में डेटा लोडिंग भी शामिल करनी चाहिए क्योंकि यह एक बाधा हो सकती है, यानी 2 कोर आपकी मेमोरी बस को संतृप्त कर सकते हैं। (इसके अलावा, यदि आप इसे पहले से नहीं कर रहे हैं, तो आपको अपने टाइमर को विभिन्न कैश-लाइनों पर रखना चाहिए।) – Mats

+0

@ मैट्स: प्रोसेसर इंटेल (आर) कोर (टीएम) 2 क्वाड सीपीयू Q9950 @ 2.83GHz है। नहीं, मैंने डेटा खंड के आकार की पुष्टि नहीं की है। ठीक है, मैं डेटा खंड के आकार को बदलने की कोशिश करूंगा। हालांकि, मुझे समझ में नहीं आया कि कैश-लाइनों से आपका क्या मतलब है। टाइमर को कैश में कैसे रखा जाए? – Junaid

उत्तर

2

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

बस यह अकेला, एक कठोर गति कम कर सकता है

यदि पर्याप्त स्मृति है, तो इनपुट डेटा का एक बड़ा हिस्सा पढ़ना हमेशा छोटे धागे में डेटा पढ़ने से तेज़ होगा, खासकर प्रत्येक थ्रेड से। जब आप इसे टुकड़ों में तोड़ते हैं तो चंकिंग (कैशिंग प्रभाव) से कोई भी I/O लाभ गायब हो जाता है। मेमोरी का एक बड़ा हिस्सा भी आवंटित करना कई बार कई हिस्सों को आवंटित करने से काफी सस्ता है।

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

सूत्रण के भीतर,

  • सूत्रण संदर्भ स्विच वजह से कई धागे कारण समीपवर्ती स्मृति नहीं पढ़ मंदी
  • पैदा कर सकता है के लिए उप इष्टतम speedup
  • के रूप में दूसरों के द्वारा उल्लेख किया है, एक ठंड कैश पैदा कर सकता है

लेकिन आपके ओपी को फिर से पढ़ना, मुझे संदेह है कि मंदी के आपके डेटा इनपुट/मेमोरी आवंटन के साथ कुछ करना है। आप कहां से अपना डेटा पढ़ रहे हैं? किसी प्रकार की सॉकेट? क्या आप वाकई अपने धागे में एक से अधिक बार स्मृति आवंटित करने की आवश्यकता है?

आपके कार्यकर्ता धागे में कुछ एल्गोरिदम उप-अधिकतम/महंगा होने की संभावना है।

0

क्या आपका धागा सृजन पर शुरू हो रहा है? यदि यह मामला है, तो निम्नलिखित होगा:

जबकि आपका मूल धागा धागा बना रहा है, पहले से बनाए गए थ्रेड को चलाने के लिए शुरू हो जाएगा। जब आप टाइमरस्टॉप (थ्रेड क्रिएशन टाइमर) हिट करते हैं, तो चार निश्चित समय के लिए चला चुके हैं। तो threadCreationTime ओवरलैप threadTime[i]

जैसा कि अब है, आप नहीं जानते कि आप क्या माप रहे हैं। यह आपकी समस्या का समाधान नहीं करेगा, क्योंकि स्पष्ट रूप से आपको कोई समस्या है क्योंकि थ्रेडटाइम रैखिक रूप से वृद्धि नहीं करता है, लेकिन कम से कम आप ओवरलैपिंग समय नहीं जोड़ेंगे।

अधिक जानकारी के लिए यदि आप अपने distro पर उपलब्ध हैं तो perf tool का उपयोग कर सकते हैं। उदाहरण के लिए :

perf stat -e cache-misses <your_prog> 

और देखते हैं कि एक दो धागा संस्करण, एक तीन धागा संस्करण आदि के साथ होता है ...

+0

यह समस्या अभी भी वहां है, भले ही मैं 'थ्रेड क्रिएशनटाइम' पर विचार न करें और केवल 'थ्रेडटाइम [i] 'पर विचार करें (जिसे अब कैश-लाइनों पर दिए गए सुझावों के बाद अलग-अलग चर में विभाजित किया गया है)। उस सुझाव का पालन करने के बाद, परिणाम में सुधार हुआ लेकिन अब बाधा को स्थानांतरित कर दिया गया है। यही है, 1 एम डेटा पर प्रदर्शन लाभ अच्छा है। लेकिन 2 एम पर यह गिरता है और फिर भी 1 जी के लिए रहता है। मैं कैश-मिस देखने के लिए भी आपके सुझाव का प्रयास करूंगा। क्या आपको लगता है कि वाल्ग्रिंड मदद कर सकता है? मैं इंटेल vTune प्रोफाइलर को आजमाने की भी सोच रहा हूं। – Junaid

+0

@ जुनाइड: कैश-मिस सिर्फ एक उदाहरण है, देखने के लिए बहुत सारे काउंटर हैं। – shodanex