मूल प्रश्न
एक पाश दो छोरों की तुलना में इतना धीमा क्यों है?
निष्कर्ष:
केस 1 एक क्लासिक इंटरपोलेशन समस्या है जो एक अक्षम होने के लिए होती है। मुझे यह भी लगता है कि यह एक प्रमुख कारण था कि कई मशीन आर्किटेक्चर और डेवलपर्स ने मल्टी-थ्रेडेड एप्लिकेशन के साथ-साथ समानांतर प्रोग्रामिंग करने की क्षमता के साथ मल्टी-कोर सिस्टम का निर्माण और डिजाइनिंग को समाप्त किया।
हार्डवेयर, ओएस, और कंपाइलर (ओं) को एक साथ काम करने के लिए इस तरह के दृष्टिकोण से इसे देखने के लिए एक साथ ढेर आवंटन करने के लिए काम करता है जिसमें रैम, कैश, पेज फ़ाइलें, आदि के साथ काम करना शामिल है; गणित जो इन एल्गोरिदम की नींव में है, हमें दिखाता है कि इन दोनों में से कौन सा बेहतर समाधान है।
हम एक के एक सादृश्य का उपयोग कर सकते Bossएक किया जा रहा Summationहै कि एक का प्रतिनिधित्व करेंगी For Loopकार्यकर्ताओं के बीच यात्रा नहीं है Aऔर B।
हम आसानी से देख सकते हैं कि केस 2 कम से कम आधा उपवास है यदि यात्रा करने के लिए आवश्यक दूरी और श्रमिकों के बीच लगने वाले समय के अंतर के कारण केस 1 से थोड़ा अधिक नहीं । यह गणित बेंचमार्क टाइम्स के साथ-साथ असेंबली इंस्ट्रक्शंस में मतभेदों की संख्या के साथ लगभग पूरी तरह से और लगभग पूरी तरह से रेखाबद्ध करता है।
अब मैं यह समझाना शुरू करूंगा कि यह सब नीचे कैसे काम करता है।
समस्या का आकलन
ओपी का कोड:
const int n=100000;
for(int j=0;j<n;j++){
a1[j] += b1[j];
c1[j] += d1[j];
}
तथा
for(int j=0;j<n;j++){
a1[j] += b1[j];
}
for(int j=0;j<n;j++){
c1[j] += d1[j];
}
विचार
ओपी के मूल प्रश्न को लूप के 2 वेरिएंट के बारे में ध्यान में रखते हुए और अन्य कई उत्कृष्ट उत्तरों और उपयोगी टिप्पणियों के साथ-साथ कैश के व्यवहार के प्रति उनका संशोधित प्रश्न; मैं इस स्थिति और समस्या के बारे में एक अलग दृष्टिकोण लेकर यहाँ कुछ अलग करने की कोशिश करना चाहता हूँ।
पहुंच
दो छोरों को ध्यान में रखते हुए और कैश और पेज फाइलिंग के बारे में चर्चा करते हुए मैं इसे एक अलग दृष्टिकोण से देखने के लिए एक और दृष्टिकोण लेना चाहूंगा। एक जो कैश और पेज फ़ाइलों को शामिल नहीं करता है और न ही मेमोरी आवंटित करने के लिए निष्पादित करता है, वास्तव में, यह दृष्टिकोण वास्तविक हार्डवेयर या सॉफ़्टवेयर की बिल्कुल भी चिंता नहीं करता है।
परिदृश्य
थोड़ी देर के लिए कोड को देखने के बाद यह काफी स्पष्ट हो गया कि समस्या क्या है और यह क्या पैदा कर रहा है। आइए इसे एक एल्गोरिथम समस्या में तोड़ते हैं और इसे गणितीय अंकन का उपयोग करने के दृष्टिकोण से देखते हैं और फिर गणित की समस्याओं के साथ-साथ एल्गोरिदम पर भी लागू होते हैं।
हम क्या जानते हैं
हम जानते हैं कि यह लूप 100,000 बार चलेगा। हम यह भी जानते हैं कि a1, b1, c1और d1एक 64-बिट वास्तुकला पर संकेत दिए गए हैं। 32-बिट मशीन पर C ++ के भीतर, सभी पॉइंटर्स 4 बाइट्स हैं और 64-बिट मशीन पर, वे 8 बाइट्स आकार में हैं क्योंकि पॉइंटर्स एक निश्चित लंबाई के हैं।
हम जानते हैं कि हमारे पास 32 बाइट्स हैं, जिसमें दोनों मामलों के लिए आवंटन करना है। एकमात्र अंतर यह है कि हम प्रत्येक पुनरावृत्ति पर 32 बाइट्स या 2-8 बाइट्स के 2 सेट आवंटित कर रहे हैं जिसमें दूसरा मामला है जो हम दोनों स्वतंत्र लूप के लिए प्रत्येक पुनरावृत्ति के लिए 16 बाइट्स आवंटित कर रहे हैं।
दोनों लूप अभी भी कुल आवंटन में 32 बाइट्स के बराबर हैं। इस जानकारी के साथ अब आगे बढ़ते हैं और सामान्य गणित, एल्गोरिदम और इन अवधारणाओं का सादृश्य दिखाते हैं।
हम कई बार जानते हैं कि एक ही सेट या संचालन का समूह जिसे दोनों मामलों में प्रदर्शन करना होगा। हम दोनों मामलों में आवंटित की जाने वाली स्मृति की मात्रा को जानते हैं। हम यह आकलन कर सकते हैं कि दोनों मामलों के बीच आवंटन का कुल कार्यभार लगभग समान होगा।
जो हम नहीं जानते
हम नहीं जानते कि प्रत्येक मामले में कितना समय लगेगा जब तक कि हम एक काउंटर सेट नहीं करते हैं और बेंचमार्क टेस्ट चलाते हैं। हालाँकि, बेंचमार्क पहले से ही मूल प्रश्न से और कुछ उत्तरों और टिप्पणियों से भी शामिल थे; और हम दोनों के बीच एक महत्वपूर्ण अंतर देख सकते हैं और इस समस्या के लिए इस प्रस्ताव का पूरा तर्क है।
जांच करते हैं
यह पहले से ही स्पष्ट है कि कई ने पहले ही ढेर आवंटन, बेंचमार्क परीक्षण, रैम, कैश और पेज फ़ाइलों को देखकर ऐसा किया है। विशिष्ट डेटा बिंदुओं और विशिष्ट चलना सूचकांकों को भी शामिल किया गया था और इस विशिष्ट समस्या के बारे में विभिन्न वार्तालापों में कई लोग इसके बारे में अन्य संबंधित चीजों पर सवाल उठाने लगे हैं। हम गणितीय एल्गोरिदम का उपयोग करके और इसके लिए एक सादृश्य लागू करके इस समस्या को कैसे देखना शुरू करते हैं? हम एक दो दावे करके शुरू करते हैं! फिर हम अपने एल्गोरिथ्म का निर्माण करते हैं।
हमारे दावे:
- हम अपने लूप और उसके पुनरावृत्तियों को 1 पर शुरू होने वाले योग के रूप में देंगे और 0 के साथ शुरू होने के बजाय 100000 पर समाप्त होता है क्योंकि लूप्स में हमें 0 एड्रेसिंग स्कीम के बारे में चिंता करने की आवश्यकता नहीं है क्योंकि हम सिर्फ रुचि रखते हैं। एल्गोरिथ्म ही।
- दोनों मामलों में हमारे पास 4 कार्य हैं और 2 फ़ंक्शन कॉल के साथ 2 फ़ंक्शन प्रत्येक फ़ंक्शन कॉल पर किए जा रहे हैं। हम निम्नलिखित के रूप में कार्य करने के लिए कार्य करता है और कॉल के रूप में इन स्थापित करेगा:
F1(), F2(), f(a), f(b), f(c)और f(d)।
एल्गोरिदम:
पहला मामला: - केवल एक समन लेकिन दो स्वतंत्र फ़ंक्शन कॉल।
Sum n=1 : [1,100000] = F1(), F2();
F1() = { f(a) = f(a) + f(b); }
F2() = { f(c) = f(c) + f(d); }
दूसरा मामला: - दो योग लेकिन प्रत्येक का अपना फ़ंक्शन कॉल है।
Sum1 n=1 : [1,100000] = F1();
F1() = { f(a) = f(a) + f(b); }
Sum2 n=1 : [1,100000] = F1();
F1() = { f(c) = f(c) + f(d); }
तो आपने देखा F2()ही में मौजूद है Sumसे Case1जहां F1()में निहित है Sumसे Case1और दोनों में Sum1और Sum2से Case2। यह बाद में स्पष्ट होगा जब हम निष्कर्ष निकालना शुरू करते हैं कि एक अनुकूलन है जो दूसरे एल्गोरिथ्म के भीतर हो रहा है।
पहले केस Sumकॉल्स के माध्यम से पुनरावृत्तियों f(a)जो अपने आप को जोड़ देगा, f(b)फिर वह कॉल करता है f(c)जो ऐसा ही करेगा लेकिन f(d)प्रत्येक 100000पुनरावृत्तियों के लिए खुद को जोड़ देगा । दूसरे मामले में, हमारे पास Sum1और Sum2यह है कि दोनों एक ही कार्य करते हैं मानो वे एक ही कार्य पंक्ति में दो बार बुलाए जा रहे हों।
इस मामले में हम इलाज कर सकते हैं Sum1और Sum2सिर्फ सादे पुराने Sumजहां Sumइस मामले में इस तरह दिखता है: Sum n=1 : [1,100000] { f(a) = f(a) + f(b); }और अब यह एक अनुकूलन की तरह दिखता है जहां हम इसे केवल एक ही कार्य मान सकते हैं।
सादृश्य के साथ सारांश
दूसरे मामले में हमने जो देखा है, वह लगभग ऐसा प्रतीत होता है जैसे कि अनुकूलन है क्योंकि दोनों छोरों के लिए समान सटीक हस्ताक्षर हैं, लेकिन यह वास्तविक मुद्दा नहीं है। मुद्दा काम कर रहा है कि द्वारा किया जा रहा नहीं है f(a), f(b), f(c), और f(d)। दोनों मामलों में और दोनों के बीच की तुलना में, यह उस अंतर का अंतर है जो योग के प्रत्येक मामले में यात्रा करना है जो आपको निष्पादन के समय में अंतर देता है।
के बारे में सोचो For Loopsहोने के रूप में Summationsहै कि एक होने के रूप में पुनरावृत्तियों करता है Bossकि दो लोगों के लिए आदेश दे रहा है Aऔर Bऔर कहा कि अपनी नौकरी मांस के लिए कर रहे Cऔर Dक्रमश: और उनमें से कुछ पैकेज लेने और इसे वापस करने के लिए। इस सादृश्य में, लूप्स या समिश्र पुनरावृत्तियों और स्थिति की जाँच के लिए स्वयं वास्तव में प्रतिनिधित्व नहीं करते हैं Boss। क्या वास्तव में प्रतिनिधित्व Bossवास्तविक गणितीय एल्गोरिदम सीधे से लेकिन की वास्तविक अवधारणा से नहीं है Scopeऔर Code Blockभीतर एक नियमित या सबरूटीन, विधि, समारोह, अनुवाद इकाई, आदि पहले एल्गोरिथ्म 1 गुंजाइश जहां 2 एल्गोरिथ्म लगातार 2 स्कोप है है।
प्रत्येक कॉल पर्ची पर पहले मामले के भीतर, Bossको जाता है Aऔर आदेश देता है और Aबंद हो जाता है लाने के लिए B'sतो पैकेज Bossको जाता है Cऔर आदेश देता है एक ही है और से पैकेज प्राप्त करने के लिए Dप्रत्येक यात्रा पर।
दूसरे मामले में, सभी पैकेज प्राप्त होने तक पैकेज को Bossसीधे Aजाने और लाने के साथ काम करता है B's। फिर सभी पैकेज प्राप्त करने के लिए उसी के Bossसाथ काम करता है।CD's
चूंकि हम 8-बाइट पॉइंटर के साथ काम कर रहे हैं और हीप आवंटन से निपटने के लिए निम्नलिखित समस्या पर विचार करते हैं। मान लीजिए कि Boss100 फीट से है Aऔर वह A500 फीट से है C। हमें इस बारे में चिंता करने की आवश्यकता नहीं है कि निष्पादन के आदेश के कारण Bossशुरू से कितनी दूर है C। दोनों मामलों में, Bossशुरू में Aपहले से फिर यात्रा होती है B। यह सादृश्य यह कहने के लिए नहीं है कि यह दूरी सटीक है; यह एल्गोरिदम के कामकाज को दिखाने के लिए सिर्फ एक उपयोगी परीक्षण परिदृश्य है।
कई मामलों में जब ढेर आवंटन करते हैं और कैश और पेज फ़ाइलों के साथ काम करते हैं, तो पता स्थानों के बीच की ये दूरी अलग-अलग नहीं हो सकती है या डेटा प्रकारों और सरणी आकारों की प्रकृति के आधार पर काफी भिन्न हो सकती है।
टेस्ट मामलों:
पहला मामला: पहली यात्रा परBossशुरू में 100 फीट जाना करने के लिए पर्ची देने के लिए हैAऔरAबंद हो जाता है और उसकी बात करता है, लेकिन उसके बादBoss500 फीट यात्रा करने के लिए किया हैCउसे अपने आदेश पर्ची देने के लिए। फिर अगले पुनरावृत्ति और हर दूसरे पुनरावृत्ति के बादBossदोनों के बीच 500 फीट आगे और पीछे जाना है।
दूसरा मामला:Boss करने के लिए पहली यात्रा पर 100 फीट यात्रा करने के लिए हैA, लेकिन उसके बाद, वह पहले से ही है और बस के लिए इंतजार कर रहा हैAवापस पाने के लिए जब तक सभी स्लिप भर रहे हैं। फिरBossपहले यात्रा पर 500 फीट की यात्रा करनी होती हैCक्योंकिC500 फीट से हैA। चूँकिउसकेBoss( Summation, For Loop )साथ काम करने के बाद इसे सही कहा जाAरहा है, इसलिए वह वहीं इंतजार करता है जैसा उसनेAतब तककिया थाजब तक कि सभीC'sऑर्डर स्लिप नहीं हो जाते।
अंतर यात्रा में अंतर
const n = 100000
distTraveledOfFirst = (100 + 500) + ((n-1)*(500 + 500);
// Simplify
distTraveledOfFirst = 600 + (99999*100);
distTraveledOfFirst = 600 + 9999900;
distTraveledOfFirst = 10000500;
// Distance Traveled On First Algorithm = 10,000,500ft
distTraveledOfSecond = 100 + 500 = 600;
// Distance Traveled On Second Algorithm = 600ft;
तुलनात्मक मूल्यों की तुलना
हम आसानी से देख सकते हैं कि 600 10 मिलियन से कम है। अब, यह सटीक नहीं है, क्योंकि हम राम के पते के बीच की दूरी या जिसमें से कैश या पेज फ़ाइल के प्रत्येक अंतर पर प्रत्येक कॉल को वास्तविक अंतर नहीं पता है, कई अन्य अनदेखी चर के कारण होने जा रहा है। यह स्थिति के बारे में जागरूक होने और इसे सबसे खराब स्थिति से देखने का सिर्फ एक आकलन है।
इन नंबरों से यह लगभग ऐसा प्रतीत होगा जैसे कि अल्गोरिथम वन 99%अल्गोरिथम टू की तुलना में धीमा होना चाहिए ; हालांकि, यह केवल है Boss'sहिस्सा है या एल्गोरिदम की जिम्मेदारी है और यह वास्तविक श्रमिकों के लिए खाते में नहीं है A, B, C, और Dऔर क्या वे एक और लूप के हर यात्रा पर क्या करना है। इसलिए बॉस की नौकरी में कुल काम का लगभग 15 - 40% हिस्सा होता है। श्रमिकों के माध्यम से जो काम किया जाता है उसका बहुत बड़ा प्रभाव पड़ता है, गति दर के अंतर को लगभग 50-70% तक रखने का
अवलोकन: - दो एल्गोरिदम के बीच अंतर
इस स्थिति में, यह कार्य किए जाने की प्रक्रिया की संरचना है। यह दिखाने के लिए जाता है कि केस 2 समान फ़ंक्शन घोषणा और परिभाषा दोनों के आंशिक अनुकूलन से अधिक कुशल है जहां यह केवल चर है जो नाम से भिन्न होता है और दूरी की यात्रा की जाती है।
हम यह भी देखते हैं कि कुल दूरी में यात्रा की केस 1 बहुत आगे है की तुलना में यह है स्थिति 2 और हम विचार कर सकते हैं इस दूरी हमारे कूच समय फैक्टर दो एल्गोरिदम के बीच। केस 1 में केस 2 की तुलना में बहुत अधिक काम करना है।
यह उन ASMनिर्देशों के साक्ष्य से अवलोकनीय है जो दोनों मामलों में दिखाए गए थे। क्या पहले से ही इन मामलों के बारे में कहा गया था के साथ, इस तथ्य यह है कि में के लिए खाते में नहीं है केस 1 मालिक दोनों के लिए प्रतीक्षा करनी होगी Aऔर Cवापस पाने के लिए इससे पहले कि वह वापस लिए जा सकते हैं Aप्रत्येक यात्रा के लिए फिर से। यह भी सच है कि अगर के लिए खाते में नहीं है Aया Bतो एक बहुत ही समय लग रहा है दोनों Bossऔर अन्य कार्यकर्ता (रों) निष्क्रिय प्रतीक्षा निष्पादित करने के लिए कर रहे हैं।
में केस 2 केवल एक ही जा रहा है बेकार है Bossजब तक कार्यकर्ता वापस हो जाता है। तो यह भी एल्गोरिथ्म पर प्रभाव पड़ता है।
ओपी संशोधित प्रश्न
संपादित करें: प्रश्न बिना किसी प्रासंगिकता के निकला, क्योंकि व्यवहार गंभीर रूप से सरणियों (एन) और सीपीयू कैश के आकार पर निर्भर करता है। इसलिए अगर आगे कोई दिलचस्पी है, तो मैं इस सवाल का जवाब देता हूं:
क्या आप विवरण में कुछ ठोस जानकारी प्रदान कर सकते हैं, जो निम्न ग्राफ़ पर पांच क्षेत्रों द्वारा सचित्र अलग कैश व्यवहारों का नेतृत्व करते हैं?
सीपीयू / कैश आर्किटेक्चर के बीच अंतर को इंगित करना भी दिलचस्प हो सकता है, इन सीपीयू के लिए एक समान ग्राफ प्रदान करके।
इन सवालों के बारे में
जैसा कि मैंने बिना किसी संदेह के प्रदर्शन किया है, हार्डवेयर और सॉफ्टवेयर के शामिल होने से पहले भी एक अंतर्निहित मुद्दा है।
अब मेमोरी और कैशिंग के साथ-साथ पेज फाइल आदि के प्रबंधन के लिए, जो सभी निम्नलिखित के बीच सिस्टम के एक एकीकृत सेट में एक साथ काम करते हैं:
The Architecture {हार्डवेयर, फ़र्मवेयर, कुछ एंबेडेड ड्राइवर, कर्नेल और एएसएम इंस्ट्रक्शन सेट्स}।
The OS{फ़ाइल और मेमोरी प्रबंधन प्रणाली, ड्राइवर और रजिस्ट्री}।
The Compiler {अनुवाद इकाइयाँ और स्रोत कोड के अनुकूलन}।
- और यहां तक कि
Source Codeविशिष्ट एल्गोरिदम के अपने सेट (ओं) के साथ भी ।
हम पहले से ही देख सकते हैं एक टोंटी है कि पहले एल्गोरिथ्म के भीतर क्या हो रहा है इससे पहले कि हम भी किसी भी मनमाने ढंग से साथ किसी भी मशीन पर लागू है कि वहाँ Architecture, OSहै, और Programmable Languageदूसरा एल्गोरिथ्म की तुलना में। आधुनिक कंप्यूटर के आंतरिक भाग को शामिल करने से पहले ही एक समस्या थी।
अंतिम परिणाम
तथापि; यह कहना नहीं है कि ये नए प्रश्न महत्व के नहीं हैं क्योंकि वे स्वयं हैं और वे एक भूमिका निभाते हैं। वे प्रक्रियाओं और समग्र प्रदर्शन पर प्रभाव डालते हैं और यह उन लोगों से अलग-अलग रेखांकन और आकलन से स्पष्ट होता है जिन्होंने अपने उत्तर (या) और टिप्पणी (ओं) को दिया है।
यदि आपने Bossऔर दो श्रमिकों की सादृश्य पर ध्यान दिया Aऔर Bजिन्हें जाना था Cऔर Dक्रमशः और दो एल्गोरिदम के गणितीय संकेतन पर विचार करके पैकेज प्राप्त करना था ; आप कंप्यूटर हार्डवेयर और सॉफ्टवेयर की भागीदारी के बिना देख सकते हैं की तुलना में Case 2लगभग 60%तेज है Case 1।
जब आप इन एल्गोरिदम के बाद ग्राफ और चार्ट को देखते हैं तो हार्डवेयर के दिए गए टुकड़े पर अपना संचालन करने के लिए ओएस के माध्यम से संकलित, अनुकूलित, और क्रियान्वित किया जाता है, आप अंतर के बीच थोड़ा और गिरावट देख सकते हैं। इन एल्गोरिदम में।
अगर Dataसेट काफी छोटा है, तो यह सब एक अंतर का बुरा नहीं लग सकता है। हालांकि, चूंकि हम समय के निष्पादन में अंतर के मामले में इस फ़ंक्शन की वृद्धि को देखने की तुलना Case 1में 60 - 70%धीमी हैं।Case 2
DeltaTimeDifference approximately = Loop1(time) - Loop2(time)
//where
Loop1(time) = Loop2(time) + (Loop2(time)*[0.6,0.7]) // approximately
// So when we substitute this back into the difference equation we end up with
DeltaTimeDifference approximately = (Loop2(time) + (Loop2(time)*[0.6,0.7])) - Loop2(time)
// And finally we can simplify this to
DeltaTimeDifference approximately = [0.6,0.7]*Loop2(time)
यह अनुमान सॉफ्टवेयर अनुकूलन और मशीन निर्देशों को शामिल करते हुए एल्गोरिदम और मशीन संचालन दोनों इन दो छोरों के बीच औसत अंतर है।
जब डेटा सेट रैखिक रूप से बढ़ता है, तो दोनों के बीच के समय में अंतर होता है। जब एल्गोरिथ्म 1 एल्गोरिथ्म 2 की तुलना में अधिक फ़ेच जो स्पष्ट है है Bossके बीच यात्रा आगे और पीछे अधिकतम दूरी के लिए है Aऔर Cपहली यात्रा के बाद के लिए हर यात्रा, जबकि एल्गोरिथ्म 2 Bossहै के लिए यात्रा करने के लिए Aएक बार और फिर से किया जा रहा है के बाद Aवह यात्रा करने के लिए है अधिकतम दूरी केवल एक बार जब से जा रही Aहो C।
के लिए कोशिश कर रहा है Bossएक बार और उन्हें आगे और पीछे से खेल के बजाय लगातार समान कार्य पर ध्यान केंद्रित कर उसे काफी दिन के अंत के बाद से वह यात्रा और काम करने के लिए किया था दो बार के रूप में ज्यादा से नाराज बनाने के लिए जा रहा है पर इसी तरह के दो काम करने पर ध्यान केंद्रित कर। इसलिए अपने बॉस को एक अंतर्विरोधी अड़चन में पड़ने से स्थिति का दायरा न खोएं क्योंकि बॉस का जीवनसाथी और बच्चे इसकी सराहना नहीं करेंगे।
संशोधन: सॉफ्टवेयर इंजीनियरिंग डिजाइन सिद्धांत
- छोरों के लिए पुनरावृत्तियों के भीतर Local Stackऔर Heap Allocatedअभिकलन के बीच का अंतर और उनके उपयोग, उनकी प्रभावकारिता और प्रभावशीलता के बीच अंतर - और
गणितीय एल्गोरिथ्म जो मैंने ऊपर प्रस्तावित किया था, मुख्य रूप से उन छोरों पर लागू होता है जो ढेर पर आवंटित डेटा पर संचालन करते हैं।
- लगातार ढेर संचालन:
- यदि लूप एकल कोड ब्लॉक या स्कोप के भीतर स्थानीय रूप से डेटा पर संचालन कर रहे हैं जो स्टैक फ्रेम के भीतर है तो यह अभी भी लागू होगा, लेकिन मेमोरी लोकेशन बहुत करीब हैं जहां वे आमतौर पर अनुक्रमिक हैं और दूरी की यात्रा या निष्पादन समय में अंतर है। लगभग नगण्य है। चूंकि ढेर के भीतर कोई आवंटन नहीं किया जा रहा है, मेमोरी बिखरी हुई नहीं है, और मेमोरी को राम के माध्यम से नहीं लाया जा रहा है। मेमोरी आमतौर पर स्टैक फ्रेम और स्टैक पॉइंटर के सापेक्ष अनुक्रमिक और सापेक्ष होती है।
- जब स्टैक पर लगातार संचालन किया जा रहा है, तो एक आधुनिक प्रोसेसर स्थानीय कैश रजिस्टर में इन मूल्यों को रखते हुए दोहराए जाने वाले मूल्यों और पते को कैश करेगा। यहां संचालन या निर्देशों का समय नैनो-सेकंड के क्रम पर है।
- लगातार ढेर आवंटित संचालन:
- जब आप ढेर आवंटन लागू करना शुरू करते हैं और प्रोसेसर को सीपीयू, बस कंट्रोलर और राम मॉड्यूल की वास्तुकला के आधार पर मेमोरी कॉल्स को लगातार कॉल पर लाना पड़ता है, तो ऑपरेशन के समय या निष्पादन माइक्रो के आदेश पर हो सकते हैं मिलीसेकेंड। कैश्ड स्टैक संचालन की तुलना में, ये काफी धीमी हैं।
- सीपीयू को राम से मेमोरी पता प्राप्त करना होगा और आमतौर पर सीपीयू के भीतर आंतरिक डेटा पथ या डेटा बस की तुलना में सिस्टम बस में कुछ भी धीमा होता है।
इसलिए जब आप ऐसे डेटा के साथ काम कर रहे होते हैं जो ढेर पर होना चाहिए और आप उन्हें लूप में देख रहे हैं, तो प्रत्येक डेटा सेट और उसके संबंधित एल्गोरिदम को अपने एकल लूप के भीतर रखना अधिक कुशल है। आप एक ही लूप में ढेर पर हैं कि विभिन्न डेटा सेट के कई संचालन डालकर लगातार छोरों को बाहर करने की कोशिश करने की तुलना में बेहतर अनुकूलन मिलेगा।
यह डेटा के साथ ऐसा करना ठीक है जो स्टैक पर होता है क्योंकि वे अक्सर कैश होते हैं, लेकिन ऐसे डेटा के लिए नहीं जिन्हें इसके मेमोरी एड्रेस को हर पुनरावृत्ति के लिए क्वेरी करना पड़ता है।
यह वह जगह है जहां सॉफ्टवेयर इंजीनियरिंग और सॉफ्टवेयर आर्किटेक्चर डिजाइन खेल में आता है। यह आपके डेटा को कैसे व्यवस्थित करना है, यह जानने की क्षमता है कि आपके डेटा को कैश करना कब जानना है, कब अपने डेटा को ढेर पर आवंटित करना है, अपने एल्गोरिदम को कैसे डिज़ाइन करना है और कैसे लागू करना है, यह जानना और उन्हें कब और कहाँ कॉल करना है, यह जानना।
आपके पास एक ही एल्गोरिथ्म हो सकता है जो एक ही डेटा सेट से संबंधित है, लेकिन आप इसके स्टैक वेरिएंट के लिए एक कार्यान्वयन डिजाइन और दूसरा उसके ढेर-आवंटित संस्करण के लिए बस इसलिए चाहते हैं क्योंकि उपरोक्त समस्या जो O(n)एल्गोरिथ्म की अपनी जटिलता से देखी जाती है जब काम कर रही हो ढेर के साथ।
वर्षों से मैंने जो देखा है, उससे कई लोग इस तथ्य को ध्यान में नहीं रखते हैं। वे एक एल्गोरिथ्म को डिज़ाइन करते हैं जो किसी विशेष डेटा सेट पर काम करता है और वे इसका उपयोग उस डेटा की परवाह किए बिना करेंगे, जो कि स्टैक पर स्थानीय रूप से कैश्ड सेट किए जाने पर या यदि यह ढेर पर आवंटित किया गया है।
यदि आप सही अनुकूलन चाहते हैं, हाँ यह कोड दोहराव जैसा लग सकता है, लेकिन इसे सामान्य करने के लिए एक ही एल्गोरिथ्म के दो वेरिएंट होना अधिक कुशल होगा। स्टैक ऑपरेशन्स के लिए एक, और हीप ऑपरेशन्स के लिए दूसरा जो पुनरावृत्त छोरों में किया जाता है!
यहाँ एक छद्म उदाहरण है: दो सरल संरचनाएं, एक एल्गोरिथ्म।
struct A {
int data;
A() : data{0}{}
A(int a) : data{a}{}
};
struct B {
int data;
B() : data{0}{}
A(int b) : data{b}{}
}
template<typename T>
void Foo( T& t ) {
// do something with t
}
// some looping operation: first stack then heap.
// stack data:
A dataSetA[10] = {};
B dataSetB[10] = {};
// For stack operations this is okay and efficient
for (int i = 0; i < 10; i++ ) {
Foo(dataSetA[i]);
Foo(dataSetB[i]);
}
// If the above two were on the heap then performing
// the same algorithm to both within the same loop
// will create that bottleneck
A* dataSetA = new [] A();
B* dataSetB = new [] B();
for ( int i = 0; i < 10; i++ ) {
Foo(dataSetA[i]); // dataSetA is on the heap here
Foo(dataSetB[i]); // dataSetB is on the heap here
} // this will be inefficient.
// To improve the efficiency above, put them into separate loops...
for (int i = 0; i < 10; i++ ) {
Foo(dataSetA[i]);
}
for (int i = 0; i < 10; i++ ) {
Foo(dataSetB[i]);
}
// This will be much more efficient than above.
// The code isn't perfect syntax, it's only psuedo code
// to illustrate a point.
यह वही है जो मैं स्टैक वेरिएंट बनाम हीप वेरिएंट के लिए अलग-अलग कार्यान्वयन होने का उल्लेख कर रहा था। एल्गोरिदम खुद बहुत ज्यादा मायने नहीं रखता है, यह लूपिंग संरचनाएं हैं जो आप उन्हें उस काम में उपयोग करेंगे।