इससे मुझे आश्चर्य हुआ कि मौजूदा उद्योग परिदृश्य में मल्टीथ्रेडिंग कितना महत्वपूर्ण है?
प्रदर्शन-महत्वपूर्ण क्षेत्रों में जहां प्रदर्शन भारी-भार उठाने वाले तीसरे पक्ष के कोड से नहीं हो रहा है, लेकिन हमारा अपना है, तो मैं CPU परिप्रेक्ष्य से महत्वपूर्ण महत्व के इस क्रम में चीजों पर विचार करना चाहता हूं (GPU एक वाइल्डकार्ड है जिसे मैंने जीता है 'में नहीं जाना):
- मेमोरी दक्षता (पूर्व: संदर्भ का स्थानीयता)।
- एल्गोरिथम
- बहु सूत्रण
- SIMD
- अन्य अनुकूलन (स्थिर शाखा पूर्वानुमान संकेत, उदाहरण के लिए)
ध्यान दें कि यह सूची पूरी तरह से महत्व पर आधारित नहीं है, लेकिन रखरखाव पर उनके प्रभाव की तरह बहुत से अन्य गतिशीलता, वे कितने सीधे हैं (यदि नहीं, तो पहले से अधिक विचार करने लायक है), सूची में दूसरों के साथ उनकी बातचीत आदि।
मेमोरी क्षमता
एल्गोरिथम पर स्मृति दक्षता के मेरी पसंद पर सबसे अधिक आश्चर्य हो सकता है। ऐसा इसलिए है क्योंकि स्मृति दक्षता इस सूची में सभी 4 अन्य मदों के साथ बातचीत करती है, और यह इसलिए है क्योंकि "कार्यान्वयन" श्रेणी के बजाय "डिजाइन" श्रेणी में इसका अक्सर विचार होता है। स्मृति दक्षता को समझने में अक्सर चिकन या अंडा समस्या का एक सा हिस्सा होता है, अक्सर सूची पर सभी 4 वस्तुओं पर विचार करने की आवश्यकता होती है, जबकि सभी 4 अन्य वस्तुओं को भी स्मृति दक्षता पर विचार करने की आवश्यकता होती है। फिर भी यह हर चीज के दिल में है।
उदाहरण के लिए, यदि हमें एक ऐसी डेटा संरचना की आवश्यकता है जो छोटे-छोटे तत्वों के लिए रैखिक-समय अनुक्रमिक पहुंच और निरंतर-समय सम्मिलन प्रदान करती है और छोटे तत्वों के लिए और कुछ नहीं है, तो यहां तक पहुंचने के लिए भोली विकल्प एक लिंक की गई सूची होगी। यह स्मृति दक्षता की अवहेलना है। जब हम मिश्रण में मेमोरी दक्षता पर विचार करते हैं, तो हम इस परिदृश्य में अधिक सन्निहित संरचनाओं को चुनते हैं, जैसे कि बढ़ने योग्य सरणी-आधारित संरचनाएं या अधिक सन्निहित नोड्स (उदा: एक नोड में 128 तत्वों को एक साथ संग्रहीत करना) या बहुत कम से कम एक पूल आवंटनकर्ता द्वारा समर्थित एक लिंक्ड सूची। समान एल्गोरिथम जटिलता होने के बावजूद इनमें नाटकीय बढ़त है। इसी तरह, हम अक्सर स्मृति दक्षता के कारण एक अवर एल्गोरिथम जटिलता के बावजूद मर्ज सॉर्ट पर एक सरणी का क्विकॉर्ट चुनते हैं।
इसी तरह, यदि हमारे मेमोरी एक्सेस पैटर्न इतने दानेदार और प्रकृति में बिखरे हुए हैं, तो हम कुशल मल्टीथ्रेडिंग नहीं कर सकते हैं, क्योंकि हम कोड में सबसे दानेदार स्तरों पर लॉक करते हुए झूठी साझाकरण की मात्रा को अधिकतम करते हैं। इसलिए मेमोरी की दक्षता दक्षता को कई गुना बढ़ा देती है। यह आउट थ्रेड का सबसे अधिक लाभ लेने के लिए एक शर्त है।
सूची में ऊपर दिए गए प्रत्येक एकल आइटम में डेटा के साथ एक जटिल इंटरैक्शन होता है, और इस बात पर ध्यान केंद्रित किया जाता है कि डेटा का प्रतिनिधित्व कैसे किया जाता है, आखिरकार मेमोरी दक्षता की नस में होता है। इनमें से हर एक को डेटा का प्रतिनिधित्व या एक्सेस करने के अनुचित तरीके से टोंटी जा सकती है।
एक और कारण स्मृति दक्षता इतना महत्वपूर्ण है कि यह पूरे कोडबेस में लागू हो सकता है । आम तौर पर जब लोग कल्पना करते हैं कि अक्षमताएँ काम के छोटे-छोटे खंडों से यहाँ-वहाँ जमा हो जाती हैं, तो यह एक संकेत है कि उन्हें एक प्रोफाइलर को हथियाने की जरूरत है। फिर भी कम-विलंबता क्षेत्र या बहुत सीमित हार्डवेयर से निपटने वाले लोग वास्तव में मिलेंगे, प्रोफाइलिंग के बाद भी, सत्र जो एक स्पष्ट कोड में स्पष्ट हॉस्पॉट्स (केवल बार-बार सभी जगह बिखरे हुए) का संकेत देते हैं, जो उस तरह से अक्षम हैं, जो नकल, नकल, और स्मृति तक पहुँचने। आमतौर पर यह केवल उसी समय होता है जब एक संपूर्ण कोडबेस एक प्रदर्शन चिंता के लिए अतिसंवेदनशील हो सकता है जो पूरे कोडबेस में लागू मानकों के एक पूरे नए सेट को जन्म दे सकता है, और मेमोरी दक्षता अक्सर इसके दिल में होती है।
एल्गोरिथम
यह एक बहुत अधिक दिया गया है, एक छँटाई एल्गोरिथ्म में पसंद एक बड़े पैमाने पर इनपुट के बीच अंतर करने के लिए महीनों बनाम सॉर्ट करने के लिए सेकंड कर सकते हैं। यह सब का सबसे बड़ा प्रभाव बनाता है अगर पसंद के बीच है, कहते हैं, वास्तव में उप-सम चतुर्भुज या घन एल्गोरिदम और एक रैखिक एक या एक रैखिक और लघुगणक या निरंतर के बीच, कम से कम जब तक हम 1,000,000 कोर मशीनों की तरह हैं (जिस स्थिति में स्मृति) दक्षता और भी महत्वपूर्ण हो जाएगी)।
यह मेरी व्यक्तिगत सूची में सबसे ऊपर नहीं है, हालाँकि, चूंकि उनके क्षेत्र में सक्षम कोई भी व्यक्ति हताशा के लिए त्वरण संरचना का उपयोग करना जानता होगा, जैसे कि हम एल्गोरिथम ज्ञान से संतृप्त हैं, और चीजों को जानना जैसे कि त्रिक के एक संस्करण का उपयोग करना उपसर्ग-आधारित खोजों के लिए एक मूलांक का पेड़ शिशु सामान है। जिस क्षेत्र में हम काम कर रहे हैं, उस तरह के बुनियादी ज्ञान को खोना, फिर एल्गोरिथम दक्षता निश्चित रूप से शीर्ष पर पहुंच जाएगी, लेकिन अक्सर एल्गोरिथम दक्षता तुच्छ होती है।
नए एल्गोरिदम का आविष्कार करना कुछ क्षेत्रों में एक आवश्यकता हो सकती है (उदा: मेष प्रसंस्करण में मुझे सैकड़ों का आविष्कार करना पड़ा है क्योंकि वे या तो पहले मौजूद नहीं थे, या अन्य उत्पादों में समान सुविधाओं के कार्यान्वयन स्वामित्व रहस्य थे, एक कागज में प्रकाशित नहीं हुए थे। )। हालाँकि, एक बार जब हम समस्या को हल करने वाले हिस्से को पार कर लेते हैं और सही परिणाम प्राप्त करने का एक तरीका खोज लेते हैं, और एक बार दक्षता लक्ष्य बन जाती है, तो वास्तव में इसे प्राप्त करने का एकमात्र तरीका यह विचार करना है कि हम डेटा (मेमोरी) के साथ कैसे सहभागिता कर रहे हैं। मेमोरी दक्षता को समझने के बिना, नया एल्गोरिथ्म इसे तेजी से बनाने के निरर्थक प्रयासों के साथ अनावश्यक रूप से जटिल हो सकता है, जब एकमात्र आवश्यक चीज यह थी कि मेमोरी दक्षता का थोड़ा और अधिक सरल, अधिक सुरुचिपूर्ण एल्गोरिदम बनाने के लिए विचार किया जाए।
अंत में, स्मृति दक्षता की तुलना में "कार्यान्वयन" श्रेणी में एल्गोरिदम अधिक होते हैं। वे अक्सर एक उप-इष्टतम एल्गोरिथ्म के साथ प्रारंभ में उपयोग किए जाने पर भी दृष्टि में सुधार करना आसान होते हैं। उदाहरण के लिए, एक हीन छवि प्रसंस्करण एल्गोरिथ्म अक्सर कोडबेस में केवल एक स्थानीय स्थान पर लागू किया जाता है। इसे बाद में बेहतर तरीके से स्वैप किया जा सकता है। हालाँकि, यदि सभी इमेज प्रोसेसिंग एल्गोरिदम एक Pixel
इंटरफेस से बंधे हैं, जिसमें एक उप-इष्टतम मेमोरी प्रतिनिधित्व है, लेकिन इसे सही करने का एकमात्र तरीका कई पिक्सल का प्रतिनिधित्व करने का तरीका बदलना है (और एक भी नहीं), तो हम अक्सर होते हैं एसओएल और एक के लिए पूरी तरह से कोडबेस को फिर से लिखना होगाImage
इंटरफेस। सॉर्टिंग एल्गोरिथ्म को बदलने के लिए एक ही तरह की बात होती है - यह आमतौर पर एक कार्यान्वयन विवरण होता है, जबकि डेटा के अंतर्निहित प्रतिनिधित्व के लिए एक पूर्ण परिवर्तन को सॉर्ट किया जा रहा है या जिस तरह से संदेशों के माध्यम से पारित किया गया है, उसे पुन: डिज़ाइन करने के लिए इंटरफेस की आवश्यकता हो सकती है।
बहु सूत्रण
मल्टीथ्रेडिंग प्रदर्शन के संदर्भ में एक कठिन है क्योंकि यह हार्डवेयर विशेषताओं के लिए एक सूक्ष्म-स्तरीय अनुकूलन है, लेकिन हमारा हार्डवेयर वास्तव में उस दिशा में बढ़ रहा है। पहले से ही मेरे पास सहकर्मी हैं जिनके पास 32 कोर हैं (मेरे पास केवल 4 हैं)।
अभी तक mulithreading सबसे खतरनाक माइक्रो-ऑप्टिमाइज़ेशन में से एक है जो शायद एक पेशेवर के लिए जाना जाता है यदि उद्देश्य सॉफ़्टवेयर को गति देने के लिए उपयोग किया जाता है। दौड़ की स्थिति बहुत अधिक घातक बग संभव है, क्योंकि यह प्रकृति में इतना अनिश्चित है (शायद एक डिबगिंग संदर्भ के बाहर सबसे असुविधाजनक समय पर एक डेवलपर की मशीन पर हर कुछ महीने में दिखाई दे रहा है, यदि बिल्कुल भी)। तो यकीनन इन सभी के बीच कोड की स्थिरता और संभावित शुद्धता पर सबसे नकारात्मक गिरावट है, खासकर जब से मल्टीथ्रेडिंग से संबंधित बग आसानी से सबसे सावधान परीक्षण के रडार के नीचे भी उड़ सकते हैं।
फिर भी, यह इतना महत्वपूर्ण होता जा रहा है। हालांकि यह अभी भी स्मृति दक्षता (जो कभी-कभी चीजों को सौ गुना तेज बना सकती है) जैसी कुछ चीज़ों को ट्रम्प नहीं कर सकता है, जो कि हमारे पास अभी है, हम अधिक से अधिक कोर देख रहे हैं। बेशक, यहां तक कि 100-कोर मशीनों के साथ, मैं अभी भी सूची के शीर्ष पर मेमोरी दक्षता डालूंगा, क्योंकि थ्रेड दक्षता आमतौर पर इसके बिना असंभव है। एक प्रोग्राम ऐसी मशीन पर सौ थ्रेड का उपयोग कर सकता है और फिर भी धीमी गति से कुशल मेमोरी प्रतिनिधित्व और एक्सेस पैटर्न (जो लॉकिंग पैटर्न में टाई जाएगा) का अभाव है।
SIMD
SIMD भी थोड़ा अजीब है क्योंकि रजिस्टर वास्तव में व्यापक हो रहे हैं, और भी व्यापक होने की योजना है। मूल रूप से हमने 64-बिट एमएमएक्स रजिस्टर देखा, जिसके बाद समानांतर में 4 एसपीएफपी संचालन में सक्षम 128-बिट एक्सएमएम रजिस्टर। अब हम समांतर 8 में सक्षम 256-बिट YMM रजिस्टर देख रहे हैं। और पहले से ही 512-बिट रजिस्टरों के लिए योजनाएं हैं जो 16 को समानांतर में अनुमति देगा।
ये मल्टीथ्रेडिंग की दक्षता के साथ परस्पर क्रिया और गुणा करेंगे। फिर भी SIMD बहुतायत के रूप में केवल स्थिरता बनाए रख सकता है। भले ही उनसे संबंधित कीड़े जरूरी नहीं हैं कि एक डेडलॉक या दौड़ की स्थिति के रूप में पुन: पेश करना और ठीक करना मुश्किल हो, पोर्टेबिलिटी अजीब है, और यह सुनिश्चित करना कि कोड हर किसी की मशीन पर चल सकता है (और उनकी हार्डवेयर क्षमताओं के आधार पर उचित निर्देशों का उपयोग करके) अजीब।
एक और बात यह है कि जब कंपाइलर आज आमतौर पर विशेषज्ञ लिखित SIMD कोड को नहीं हराते हैं, तो वे भोले-भाले प्रयासों को आसानी से हरा देते हैं। वे उस बिंदु पर सुधार कर सकते हैं जहां हमें अब इसे मैन्युअल रूप से नहीं करना है, या कम से कम इतना मैनुअल प्राप्त किए बिना आंतरिक या सीधे विधानसभा कोड (शायद सिर्फ थोड़ा मानव मार्गदर्शन) लिखना है।
एक बार फिर, बिना किसी मेमोरी लेआउट के, जो कि वेक्टरकृत प्रसंस्करण के लिए कुशल है, SIMD बेकार है। हम केवल एक स्केलर फ़ील्ड को एक विस्तृत रजिस्टर में लोड कर रहे हैं, केवल उस पर एक ऑपरेशन करने के लिए। इन सभी वस्तुओं के दिल में वास्तव में कुशल होने के लिए मेमोरी लेआउट पर निर्भरता है।
अन्य अनुकूलन
ये अक्सर वही होते हैं जो मैं सुझाव देता हूं कि हम आजकल "माइक्रो" कॉल करना शुरू कर देंगे, यदि शब्द न केवल एल्गोरिदमिक फोकस से परे जाने का सुझाव देता है, बल्कि उन परिवर्तनों की ओर भी है जो प्रदर्शन पर एक लघु प्रभाव डालते हैं।
अक्सर शाखा भविष्यवाणी के लिए अनुकूलन करने की कोशिश करने के लिए एल्गोरिथ्म या स्मृति दक्षता में बदलाव की आवश्यकता होती है, उदाहरण के लिए, अगर यह केवल संकेत और स्थैतिक भविष्यवाणी के लिए कोड को पुन: व्यवस्थित करने का प्रयास किया जाता है, तो यह केवल ऐसे कोड के पहली बार निष्पादन में सुधार करता है, जिससे प्रभावों पर सवाल उठता है अक्सर एकमुश्त नगण्य नहीं।
प्रदर्शन के लिए वापस मल्टीथ्रेडिंग पर जाएं
तो वैसे भी, एक प्रदर्शन के संदर्भ से कितना महत्वपूर्ण है? मेरे 4-कोर मशीन पर, यह आदर्श रूप से 5 गुना तेजी से चीजें बना सकता है (हाइपरथ्रेडिंग के साथ मुझे क्या मिल सकता है)। यह मेरे सहयोगी के लिए काफी महत्वपूर्ण होगा जिनके पास 32 कोर हैं। और यह आने वाले वर्षों में तेजी से महत्वपूर्ण हो जाएगा।
इसलिए यह काफी महत्वपूर्ण है। लेकिन यह समस्या पर थ्रेड्स का एक गुच्छा फेंकना बेकार है यदि मेमोरी दक्षता नहीं है तो तालों को संयम से इस्तेमाल किया जा सकता है, झूठी साझाकरण को कम करने के लिए, आदि।
प्रदर्शन के बाहर बहुआयामी
मल्टीथ्रेडिंग हमेशा सरासर प्रदर्शन के बारे में एक सीधी तरह से समझ में नहीं आता है। कभी-कभी यह उपयोगकर्ता को जवाबदेही में सुधार करने के लिए थ्रूपुट की संभावित लागत पर भी एक लोड को संतुलित करने के लिए उपयोग किया जाता है, या उपयोगकर्ता को चीजों को खत्म करने के लिए इंतजार किए बिना अधिक मल्टीटास्किंग करने की अनुमति देता है (उदा: फ़ाइल डाउनलोड करते समय ब्राउज़िंग जारी रखें)।
उन मामलों में, मेरा सुझाव है कि मल्टीथ्रेडिंग ऊपर की ओर बहुत अधिक बढ़ जाता है (संभवतः मेमोरी दक्षता से भी ऊपर), क्योंकि यह तब हार्डवेयर के सबसे बाहर निकलने के बजाय उपयोगकर्ता के अंत डिजाइन के बारे में है। यह अक्सर इंटरफ़ेस डिज़ाइनों पर हावी होता जा रहा है और जिस तरह से हम इस तरह के परिदृश्यों में हमारे पूरे कोडबेस को संरचना देते हैं।
जब हम बड़े पैमाने पर डेटा संरचना तक पहुँचने के लिए बस एक तंग लूप को समानांतर नहीं कर रहे हैं, तो मल्टीथ्रेडिंग वास्तव में कट्टर "डिज़ाइन" श्रेणी में चला जाता है, और डिज़ाइन हमेशा ट्रम्प कार्यान्वयन होता है।
तो उन मामलों में, मैं कहूंगा कि मल्टीथ्रेडिंग अपफ्रंट पर विचार करना बहुत महत्वपूर्ण है, स्मृति प्रतिनिधित्व और पहुंच से भी अधिक।