मूल प्रश्न
एक पाश दो छोरों की तुलना में इतना धीमा क्यों है?
निष्कर्ष:
केस 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
साथ काम करता है।C
D's
चूंकि हम 8-बाइट पॉइंटर के साथ काम कर रहे हैं और हीप आवंटन से निपटने के लिए निम्नलिखित समस्या पर विचार करते हैं। मान लीजिए कि Boss
100 फीट से है A
और वह A
500 फीट से है C
। हमें इस बारे में चिंता करने की आवश्यकता नहीं है कि निष्पादन के आदेश के कारण Boss
शुरू से कितनी दूर है C
। दोनों मामलों में, Boss
शुरू में A
पहले से फिर यात्रा होती है B
। यह सादृश्य यह कहने के लिए नहीं है कि यह दूरी सटीक है; यह एल्गोरिदम के कामकाज को दिखाने के लिए सिर्फ एक उपयोगी परीक्षण परिदृश्य है।
कई मामलों में जब ढेर आवंटन करते हैं और कैश और पेज फ़ाइलों के साथ काम करते हैं, तो पता स्थानों के बीच की ये दूरी अलग-अलग नहीं हो सकती है या डेटा प्रकारों और सरणी आकारों की प्रकृति के आधार पर काफी भिन्न हो सकती है।
टेस्ट मामलों:
पहला मामला: पहली यात्रा परBoss
शुरू में 100 फीट जाना करने के लिए पर्ची देने के लिए हैA
औरA
बंद हो जाता है और उसकी बात करता है, लेकिन उसके बादBoss
500 फीट यात्रा करने के लिए किया हैC
उसे अपने आदेश पर्ची देने के लिए। फिर अगले पुनरावृत्ति और हर दूसरे पुनरावृत्ति के बादBoss
दोनों के बीच 500 फीट आगे और पीछे जाना है।
दूसरा मामला:Boss
करने के लिए पहली यात्रा पर 100 फीट यात्रा करने के लिए हैA
, लेकिन उसके बाद, वह पहले से ही है और बस के लिए इंतजार कर रहा हैA
वापस पाने के लिए जब तक सभी स्लिप भर रहे हैं। फिरBoss
पहले यात्रा पर 500 फीट की यात्रा करनी होती हैC
क्योंकिC
500 फीट से है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.
यह वही है जो मैं स्टैक वेरिएंट बनाम हीप वेरिएंट के लिए अलग-अलग कार्यान्वयन होने का उल्लेख कर रहा था। एल्गोरिदम खुद बहुत ज्यादा मायने नहीं रखता है, यह लूपिंग संरचनाएं हैं जो आप उन्हें उस काम में उपयोग करेंगे।