क्या सूचक का उपयोग मेमोरी ओवरहेड चर नहीं है?


29

C और C ++ जैसी भाषाओं में, चर का उपयोग करते समय हमें उस पते को संग्रहीत करने के लिए एक और मेमोरी स्थान की आवश्यकता होती है। तो क्या यह मेमोरी ओवरहेड नहीं है? इसकी भरपाई कैसे होती है? समय महत्वपूर्ण कम मेमोरी अनुप्रयोगों में पॉइंटर्स का उपयोग किया जाता है?


11
डायनेमिक मेमोरी एलोकेशन के फायदे पॉइंटर की लागत को बहुत कम कर देते हैं।
जेम्स मैक्लियोड

32
आप अन्य भाषाओं (जावा, सी #, ...) को वस्तुओं के संदर्भ में कैसे सोचते हैं? (संकेत: वे संकेत का उपयोग करते हैं)।
एरिक Eidt

6
एक सूचक रजिस्टरों में बैठ सकता है या एक तर्क के रूप में पारित किया जा सकता है। दोनों मामलों में कोई स्पष्ट मेमोरी ओवरहेड नहीं है । और पॉइंटर की गणना की जा सकती है (उदाहरण के लिए पॉइंटर अंकगणित, रिटर्निंग पॉइंटर आदि)
बेसिल स्टायरनेविच

9
पता द्वारा एक (बड़े) संरचना तर्क को पारित करने के बारे में कैसे ? यदि आप एक सूचक चर के रूप में गिनती करते हैं, तो यह कई एल्गोरिदम के लिए अपरिहार्य है, और मूल्य से संरचना को पारित करने की तुलना में बहुत कम जगह का उपयोग करता है !
PJTraill

4
यह कुछ लोगों के होमवर्क असाइनमेंट की भावना है। सवाल यह पता लगाने के लिए डिज़ाइन किया गया है कि क्या उत्तर संकेत को समझता है और उनका उपयोग कैसे किया जाता है।
माइकल शॉ

जवाबों:


34

वास्तव में, ओवरहेड वास्तव में पॉइंटर को स्टोर करने के लिए आवश्यक अतिरिक्त 4 या 8 बाइट्स में झूठ नहीं बोलता है। अधिकांश बार पॉइंटर्स का उपयोग डायनेमिक मेमोरी आवंटन के लिए किया जाता है , जिसका अर्थ है कि हम मेमोरी के ब्लॉक को आवंटित करने के लिए एक फ़ंक्शन को आमंत्रित करते हैं, और यह फ़ंक्शन हमें एक पॉइंटर देता है जो मेमोरी के उस ब्लॉक को इंगित करता है। अपने आप में यह नया ब्लॉक काफी उपरि का प्रतिनिधित्व करता है।

अब, आपको पॉइंटर का उपयोग करने के लिए मेमोरी आवंटन में संलग्न होने की आवश्यकता नहीं है : आपके पास intघोषित रूप से या स्टैक पर एक सरणी हो सकती है , और आप विज़िट करने के लिए एक इंडेक्स के बजाय एक पॉइंटर का उपयोग कर सकते हैंint s , और यह है सभी बहुत अच्छे और सरल और कुशल। कोई मेमोरी आवंटन की आवश्यकता नहीं है, और सूचक आमतौर पर मेमोरी में उतना ही स्थान लेगा जितना कि एक पूर्णांक सूचकांक होगा।

इसके अलावा, जैसा कि यहोशू टेलर हमें एक टिप्पणी में याद दिलाता है, संकेत संदर्भ द्वारा कुछ पारित करने के लिए उपयोग किया जाता है। जैसे, struct foo f; init_foo(&f);स्टैक पर f आवंटित करेगा और फिर init_foo()एक पॉइंटर के साथ उस पर कॉल करेगा struct। यह बहुत आम है। (बस उन बिंदुओं को "ऊपर की ओर" पास न करने के लिए सावधान रहें।) सी ++ में आप इसे "संदर्भ" के साथ किया जा सकता है (foo& एक संकेतक के बजाय ) के , लेकिन संदर्भ कुछ भी नहीं है लेकिन संकेत हैं कि आप बदल नहीं सकते हैं, और वे कब्जा कर लेते हैं स्मृति की एक ही राशि।

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

इसके अलावा, C ++ एक ऑब्जेक्ट-ओरिएंटेड भाषा है, और OOP के कुछ पहलू हैं जैसे कि एब्सट्रैक्शन जो केवल पॉइंटर्स का उपयोग करके प्राप्त करने योग्य हैं। यहां तक ​​कि जावा और सी # जैसी भाषाएं पॉइंटर्स का व्यापक उपयोग करती हैं, वे आपको सीधे पॉइंटर्स में हेरफेर करने की अनुमति नहीं देते हैं, ताकि आप उनके साथ खतरनाक चीजें करने से रोक सकें, लेकिन फिर भी, ये भाषाएं केवल एक बार आपके लिए समझ में आने लगती हैं एहसास हुआ कि पर्दे के पीछे सब कुछ पॉइंटर्स का उपयोग करके किया जाता है।

तो, संकेत कर रहे हैं नहीं केवल समय महत्वपूर्ण, कम स्मृति अनुप्रयोगों में प्रयोग किया, वे उपयोग किया जाता है हर जगह


5
"मुख्य कारण है कि पॉइंटर्स का उपयोग गतिशील मेमोरी आवंटन के लिए किया जाता है" सी ++ में ऐसा हो सकता है, लेकिन जरूरी नहीं कि सी में सी। पॉइंटर्स संदर्भ के माध्यम से कुछ पास करने का एकमात्र तरीका है। यदि आप संपूर्ण संरचना की प्रतिलिपि नहीं बनाना चाहते हैं, तो आपको इसके लिए एक संकेतक की आवश्यकता होगी। अगर आप कोई गतिशील आवंटन नहीं कर रहे हैं तब भी यह समझ में आता है। उदाहरण के लिए, स्टैक पर struct foo f; init_foo(&f);आवंटित fकिया जाएगा और फिर init_fooउस संरचना के लिए एक पॉइंटर के साथ कॉल किया जाएगा। यह बहुत आम है। (बस उन बिंदुओं को "ऊपर की ओर" नहीं पारित करने के लिए सावधान रहें।)
यहोशू टेलर

@JoshuaTaylor जो बहुत सही है, मैं इसके बारे में भूल गया था। क्या मैं इसे अपने उत्तर में संशोधन कर सकता हूं? (यह प्रोग्रामर एसई पर अच्छा अभ्यास माना जाता है क्योंकि टिप्पणियां अल्पकालिक हैं, जबकि उत्तर नहीं हैं।)
माइक नाइस

हर तरह से इसे अपना जवाब जोड़ें। :)
यहोशू टेलर

2
यह अपने आप में और काफी हद तक प्रतिनिधित्व करता है, क्योंकि इस ब्लॉक में एक (छिपा हुआ) हेडर होगा जो आमतौर पर कुछ पॉइंटर्स जितना बड़ा होगा। => वास्तव में, आधुनिक कार्यान्वयन में mallocबहुत कम हेडर ओवरहेड होते हैं क्योंकि वे "बकेट" में ब्लॉक आवंटित करते हैं। दूसरी ओर, यह सामान्य रूप से ओवर-आवंटन में तब्दील हो जाता है: आप 35 बाइट्स मांगते हैं और 64 प्राप्त करते हैं (आपकी जानकारी के बिना) इस प्रकार 29 बर्बाद कर रहे हैं ...
मैथ्यू एम।

1
@ मायकेनकीस: बेहतर लग रहा है, मेरे साथ चिपके रहने के लिए धन्यवाद :)
मैथ्यू एम।

35

तो क्या यह मेमोरी ओवरहेड नहीं है?

ज़रूर, एक अतिरिक्त पता (आमतौर पर प्रोसेसर के आधार पर 4/8 बाइट्स)।

इसकी भरपाई कैसे होती है?

यह नहीं। यदि आपको संकेत के लिए आवश्यक अप्रत्यक्ष की आवश्यकता है, तो आपको इसके लिए भुगतान करना होगा।

समय महत्वपूर्ण कम मेमोरी अनुप्रयोगों में पॉइंटर्स का उपयोग किया जाता है?

मैंने वहां ज्यादा काम नहीं किया है, लेकिन मैं ऐसा मानूंगा। पॉइंटर एक्सेस असेंबली प्रोग्रामिंग का एक प्राथमिक पहलू है। यह स्मृति की तुच्छ मात्रा लेता है और पॉइंटर संचालन तेजी से होता है - यहां तक ​​कि इन प्रकार के अनुप्रयोगों के संदर्भ में भी।


1
@DavidGrinberg यह केवल मान रहा है कि कोई वापसी मूल्य अनुकूलन नहीं है
डार्कहॉग

3
DOS के लिए TSR एप्लिकेशन लिखते समय मैंने पॉइंटर्स का उपयोग किया था जो अस्सी के दशक में 15k में फिट होना था। तो हाँ, उनका उपयोग कम-मेमोरी अनुप्रयोगों में किया जाता है।
रोबोट

1
@StevenBurnap आप TSR ऐप के लिए 15k कम मेमोरी कहते हैं? मैंने एक बार एक टीएसआर टूल लिखा था जिसमें केवल 16 बाइट्स मेमोरी की खपत थी।
कैस्परल्ड

22
@kasperd: और मेरे दिन में वापस, हमारे पास केवल शून्य था
जैक


11

मैं काफी इस पर Telastyn के रूप में एक ही स्पिन नहीं है।

एक एम्बेडेड प्रोसेसर में सिस्टम ग्लोबल्स को विशिष्ट, हार्ड-कोडित पते के साथ संबोधित किया जा सकता है।

एक कार्यक्रम में ग्लोबल्स को एक विशेष सूचक से ऑफसेट के रूप में संबोधित किया जाएगा जो स्मृति में उस जगह को इंगित करता है जहां ग्लोबल्स और स्टेटिक्स संग्रहीत किए जाते हैं।

जब एक फ़ंक्शन में प्रवेश किया जाता है तो स्थानीय चर दिखाई देते हैं और एक अन्य विशेष सूचक से ऑफसेट के रूप में संबोधित किया जाता है, जिसे अक्सर "फ्रेम पॉइंटर" कहा जाता है। इसमें फ़ंक्शन के तर्क शामिल हैं। यदि आप स्टैक पॉइंटर के साथ धक्का और चबूतरे के बारे में सावधान हैं, तो आप फ्रेम पॉइंटर से दूर कर सकते हैं और स्टैक पॉइंटर से सीधे स्थानीय चर का उपयोग कर सकते हैं।

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


6

हां बिल्कुल। लेकिन यह एक बैलेंसिंग एक्ट है।

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

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


5

C और C ++ जैसी भाषाओं में, चर का उपयोग करते समय हमें उस पते को संग्रहीत करने के लिए एक और मेमोरी स्थान की आवश्यकता होती है। तो क्या यह मेमोरी ओवरहेड नहीं है?

आप मानते हैं कि सूचक को संग्रहीत करने की आवश्यकता है। यह हमेशा की घटना नहीं है। प्रत्येक चर को किसी स्मृति पते पर संग्रहीत किया जाता है। कहो कि तुम एक के longरूप में घोषित किया है long n = 5L;। यह nकुछ पते पर भंडारण का आवंटन करता है । हम उस पते का उपयोग फैंसी चीजें करने के लिए कर सकते हैं जैसे *((char *) &n) = (char) 0xFF;कि भागों के हेरफेर करना n। का पता nएक अतिरिक्त भूमि के ऊपर के रूप में कहीं भी संग्रहीत नहीं है।

इसकी भरपाई कैसे होती है?

यहां तक ​​कि अगर पॉइंटर्स स्पष्ट रूप से संग्रहीत हैं (उदाहरण के लिए डेटा संरचनाओं जैसे सूचियों), तो परिणामस्वरूप डेटा संरचना अक्सर पॉइंटर्स के बिना समकक्ष डेटा संरचना की तुलना में अधिक सुरुचिपूर्ण (सरल, समझने में आसान, संभालना आसान है, आदि) है।

समय महत्वपूर्ण कम मेमोरी अनुप्रयोगों में पॉइंटर्स का उपयोग किया जाता है?

हाँ। माइक्रो-कंट्रोलर्स का उपयोग करने वाले उपकरणों में अक्सर बहुत कम मेमोरी होती है, लेकिन फ़र्मवेयर वेटर या बफर प्रबंधन, आदि को संभालने के लिए फर्मवेयर का उपयोग कर सकता है।


कुछ चर स्मृति में संग्रहीत नहीं किए जाते हैं, लेकिन केवल रजिस्टरों में
बासीले स्टायरेंविच

@BasileStarynkevitch: आप सुझाव दे सकते हैं कि चर रजिस्टरों में रहते हैं, लेकिन कंपाइलर को ऐसा करने की आवश्यकता नहीं है। जब तक आप असेम्बलर में प्रोग्रामिंग नहीं करते, तब तक आपके पास तत्काल स्टोरेज पर नियंत्रण का स्तर नहीं होता। और यहां तक कि कोडांतरक में, किसी भी सबरूटीन आप आह्वान संभावना रजिस्टरों ढेर पर गिर तो यह उनके लिए उपयोग कर सकते हैं अपने चर। किसी भी गैर-तुच्छ कार्यक्रम के लिए, यह लगभग गारंटी है कि आपके चर स्मृति में कम से कम कुछ समय बिताएंगे।
TMN

कंपाइलर को केवल रजिस्टरों में कुछ स्थानीय वेरिएबल्स को स्टोर करने की अनुमति दी जा सकती है, और अधिकांश ऑप्टिमाइज़िंग कंपाइलर ऐसा कर रहे हैं ( gcc -fverbose-asm -S -O2कुछ C कोड को संकलित करने की कोशिश करें )
Basile Starynkevitch

@BasileStarynkevitch मैं उस बिंदु के बारे में सुनिश्चित नहीं हूं, जिसे आप इस अवलोकन के साथ बनाने की कोशिश कर रहे हैं कि चर विशुद्ध रूप से रजिस्टरों में संग्रहीत किए जा सकते हैं। क्या आप कृपया विस्तार से बता सकते हैं?
लॉरेंस

5

पॉइंटर होने से कुछ ओवरहेड का सेवन जरूर होता है, लेकिन आप उल्टा भी देख सकते हैं। पॉइंटर इंडेक्स की तरह है। सी में आप केवल बिंदुओं के कारण स्ट्रिंग और संरचनाओं जैसी जटिल डेटा संरचनाओं का उपयोग कर सकते हैं।

वास्तव में मान लें कि आप किसी चर को संदर्भ द्वारा पास करना चाहते हैं, तो पूरी संरचना की नकल करने और उनके बीच परिवर्तनों को सिंक्रनाइज़ करने के बजाय एक सूचक बनाए रखना आसान है (यहां तक ​​कि उनकी नकल करने के लिए आपको सूचक की आवश्यकता होगी)। आप सूचक के बिना गैर सन्निहित स्मृति आवंटन और डी-आवंटन से कैसे निपटेंगे?

यहां तक ​​कि आपके सामान्य चर में प्रतीक तालिका में एक प्रविष्टि है जो पते को संग्रहीत करता है जहां आपका चर इंगित कर रहा है। इसलिए, मुझे नहीं लगता कि यह स्मृति के मामले में बहुत अधिक उपरि बनाता है (सिर्फ 4 या 8 बाइट्स)। यहां तक ​​कि जावा जैसी भाषाएं आंतरिक रूप से (संदर्भ) पॉइंटर्स का उपयोग करती हैं, वे आपको केवल उन्हें हेरफेर करने की अनुमति नहीं देते हैं क्योंकि यह जेवीएम को कम सुरक्षित बना देगा।

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


3

तो क्या यह मेमोरी ओवरहेड नहीं है?

हां नहीं शायद?

यह एक अजीब सवाल है क्योंकि मशीन पर मेमोरी एड्रेसिंग रेंज की कल्पना करें, और एक सॉफ्टवेयर जिसे लगातार चीजों पर नज़र रखने की ज़रूरत होती है, जहां चीजें एक तरह से मेमोरी में होती हैं जिन्हें स्टैक से नहीं जोड़ा जा सकता है।

उदाहरण के लिए, एक संगीत खिलाड़ी की कल्पना करें जहां उपयोगकर्ता द्वारा संगीत फ़ाइल को एक बटन पुश पर लोड किया गया हो और जब उपयोगकर्ता किसी अन्य संगीत फ़ाइल को लोड करने का प्रयास करता है, तो इसे अस्थिर मेमोरी से अनलोड किया जाता है।

ऑडियो डेटा कहाँ संग्रहीत किया जाता है, इस पर हम कैसे नज़र रखते हैं? हमें इसके लिए एक मेमोरी एड्रेस चाहिए। कार्यक्रम को न केवल स्मृति में ऑडियो डेटा चंक का ट्रैक रखने की आवश्यकता है, बल्कि यह स्मृति में भी है । इस प्रकार हमें एक मेमोरी एड्रेस (यानी एक पॉइंटर) रखना होगा। और मेमोरी एड्रेस के लिए आवश्यक स्टोरेज का आकार मशीन की एड्रेसिंग रेंज (पूर्व: 64-बिट एड्रेसिंग रेंज के लिए 64-बिट पॉइंटर) से मेल खाने वाला है।

तो यह "हाँ" की तरह है, इसे मेमोरी एड्रेस का ट्रैक रखने के लिए स्टोरेज की आवश्यकता होती है, लेकिन ऐसा नहीं है कि हम इसे इस तरह की डायनामिक-एलिमेंट मेमोरी के लिए टाल सकते हैं।

इसकी भरपाई कैसे होती है?

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

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

समय महत्वपूर्ण कम मेमोरी अनुप्रयोगों में पॉइंटर्स का उपयोग किया जाता है?

हां, बहुत आम तौर पर, चूंकि कई प्रदर्शन-महत्वपूर्ण एप्लिकेशन C या C ++ में लिखे गए हैं जो पॉइंटर उपयोग के प्रभुत्व हैं (वे एक स्मार्ट पॉइंटर के पीछे हो सकते हैं std::vectorया जैसे कंटेनर हो सकता है std::string, लेकिन अंतर्निहित मैकेनिक एक पॉइंटर को उबालते हैं जो उपयोग किया जाता है डायनेमिक मेमोरी ब्लॉक में पते का ट्रैक रखने के लिए)।

अब वापस इस सवाल पर:

इसकी भरपाई कैसे होती है? (भाग दो)

जब तक आप उनमें से एक लाख (जो अभी भी एक 64-बिट मशीन पर 8 मेगाबाइट है) की तरह भंडारण कर रहे हैं, तब तक पॉइंटर्स आमतौर पर सस्ते होते हैं।

* नोट के रूप में बेन ने बताया कि एक "औसत दर्जे का" 8 megs अभी भी L3 कैश का आकार है। यहाँ मैंने कुल घूंट के उपयोग के अर्थ में "औसत रूप से" अधिक उपयोग किया और मेमोरी को विशिष्ट सापेक्ष आकार बिंदुओं का एक स्वस्थ उपयोग इंगित करेगा।

जहां पॉइंटर्स महंगे मिलते हैं वे खुद पॉइंटर्स नहीं होते हैं:

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

  2. मेमोरी एक्सेस। यह चिंता करने के लिए बड़ा उपरि हो जाता है। जब भी हम पहली बार डायनामिक रूप से आवंटित मेमोरी का उपयोग करते हैं, तो एक अनिवार्य पृष्ठ दोष होता है और साथ ही कैश मेमोरी पदानुक्रम और नीचे एक रजिस्टर में मेमोरी को स्थानांतरित करने से चूक जाता है।

मेमोरी एक्सेस

मेमोरी एक्सेस एल्गोरिदम से परे प्रदर्शन के सबसे महत्वपूर्ण पहलुओं में से एक है। एएए गेम इंजन जैसे कई प्रदर्शन-महत्वपूर्ण क्षेत्र डेटा-उन्मुख अनुकूलन के प्रति अपनी ऊर्जा का एक बड़ा हिस्सा केंद्रित करते हैं जो अधिक कुशल मेमोरी एक्सेस पैटर्न और लेआउट के लिए उबालते हैं।

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

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

ऐसी भाषाओं के लिए बहुत सारे कंपाइलर इन दिनों निर्देश चयन और रजिस्टर आवंटन में एक बहुत अच्छा काम कर रहे हैं, लेकिन स्मृति प्रबंधन पर अधिक प्रत्यक्ष नियंत्रण की कमी हत्यारा हो सकती है (हालांकि अक्सर कम त्रुटि-प्रवण) और फिर भी जैसी भाषाएं बनाते हैं C और C ++ काफी लोकप्रिय है।

अप्रत्यक्ष रूप से पॉइंटर एक्सेस का अनुकूलन

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

चारों ओर पॉइंटर्स का पीछा करते हुए

मान लें कि हमारे पास एक एकल-लिंक्ड सूची है:

Foo->Bar->Baz->null

समस्या यह है कि अगर हम इन सभी नोड्स को अलग-अलग एक सामान्य-उद्देश्य आवंटन (और संभवतः सभी एक बार में) के खिलाफ आवंटित करते हैं, तो वास्तविक मेमोरी कुछ इस तरह से सरलीकृत हो सकती है (सरलीकृत आरेख):

यहां छवि विवरण दर्ज करें

जब हम चारों ओर बिंदुओं का पीछा करना शुरू करते हैं और Fooनोड तक पहुंचते हैं, तो हम एक अनिवार्य याद (और संभवतः एक पृष्ठ दोष) के साथ शुरू करते हैं, जो स्मृति क्षेत्र से अपने मेमोरी क्षेत्र से स्मृति के तेज क्षेत्रों से स्मृति के तेजी से क्षेत्रों तक बढ़ रहा है, जैसे:

यहां छवि विवरण दर्ज करें

यह हमें केवल एक मेमोरी क्षेत्र को कैश करने (संभवतः पृष्ठ) को इसके एक हिस्से तक पहुंचने और बाकी को बेदखल करने का कारण बनता है क्योंकि हम इस सूची में पॉइंटर्स का पीछा करते हैं। हालाँकि, मेमोरी एलोकेटर पर नियंत्रण रखने से, हम इस तरह की सूची को इस तरह आवंटित कर सकते हैं:

यहां छवि विवरण दर्ज करें

... और इस तरह से उस गति में काफी सुधार होता है जिस पर हम इन बिंदुओं को कम कर सकते हैं और उनके पॉइंटर की प्रक्रिया कर सकते हैं। इसलिए, बहुत अप्रत्यक्ष रूप से, हम इस तरह से पॉइंटर एक्सेस को तेज कर सकते हैं। बेशक अगर हम इन सन्दर्भों को एक सरणी में संग्रहीत करते हैं, तो हमारे पास यह समस्या पहली जगह में नहीं होगी, लेकिन यहाँ मेमोरी एलोकेटर हमें मेमोरी लेआउट पर स्पष्ट नियंत्रण देता है, जिस दिन एक लिंक किए गए ढांचे की आवश्यकता होती है।

* नोट: यह स्मृति पदानुक्रम और संदर्भ की स्थानीयता के बारे में एक बहुत ही सुस्पष्ट आरेख और चर्चा है, लेकिन उम्मीद है कि यह प्रश्न के स्तर के लिए उपयुक्त है।


1
"औसत रूप से 8 मेगाबाइट" एक आधुनिक सीपीयू पर पूरे L3 कैश का विशिष्ट आकार है
बेन Voigt

1
@BenVoigt मैं कोशिश करूंगा कि इसे थोड़ा मना कर सकूं। निश्चित रूप से यह भयावह होगा यदि प्रत्येक सूचक 32-बिट मेमोरी की

@BenVoigt ने उस हिस्से को स्पष्ट करने के लिए एक फुटनोट जोड़ा!

यह स्पष्टीकरण अच्छा लग रहा है।
बेन वोइगट

1

तो क्या यह मेमोरी ओवरहेड नहीं है?

यह वास्तव में एक स्मृति उपरिव्यय है, लेकिन एक बहुत ही छोटा (तुच्छता के बिंदु तक)।

इसकी भरपाई कैसे होती है?

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

समय महत्वपूर्ण कम मेमोरी अनुप्रयोगों में पॉइंटर्स का उपयोग किया जाता है?

हाँ।


0

आपको केवल अतिरिक्त मेमोरी उपयोग (4-8 बाइट्स प्रति सूचक, आम तौर पर) की आवश्यकता होती है, जबकि आपको उस पॉइंटर की आवश्यकता होती है। कई तकनीकें हैं जो इसे और अधिक सस्ती बनाती हैं।

सबसे बुनियादी तकनीक जो पॉइंटर्स को शक्तिशाली बनाती है, वह यह है कि आपको हर पॉइंटर रखने की आवश्यकता नहीं है। कभी-कभी आप एक पॉइंटर से पॉइंटर बनाने के लिए एक एल्गोरिथ्म का उपयोग करके कुछ और कर सकते हैं। इसका सबसे तुच्छ उदाहरण सरणी अंकगणितीय है। यदि आप 50 पूर्णांक की एक सरणी आवंटित करते हैं, तो आपको 50 पूर्णांक, प्रत्येक पूर्णांक में रखने की आवश्यकता नहीं है। आप आमतौर पर एक पॉइंटर (पहला वाला) का ट्रैक रखते हैं, और दूसरों को मक्खी पर उत्पन्न करने के लिए पॉइंटर अंकगणित का उपयोग करते हैं। कभी-कभी आप उन बिंदुओं में से एक को अस्थायी रूप से सरणी के एक विशिष्ट तत्व तक रख सकते हैं, लेकिन केवल तब जब आपको इसकी आवश्यकता हो। एक बार जब आप कर लेते हैं, तो आप इसे त्याग सकते हैं, जब तक कि आप इसे बाद में पुनर्जीवित करने के लिए पर्याप्त जानकारी रखते हैं, अगर आपको इसकी आवश्यकता है। यह तुच्छ लग सकता है, लेकिन इसका ठीक उसी प्रकार का संरक्षण उपकरण है जो आप '

बेहद तंग स्मृति स्थितियों में, इसका उपयोग लागत को कम करने के लिए किया जा सकता है। यदि आप एक में काम कर रहे हैं, तो बहुत तंग मेमोरी स्पेस , तो आपको आमतौर पर इस बात की अच्छी जानकारी होती है कि आपको कितनी वस्तुओं को हेरफेर करने की आवश्यकता है। एक समय में पूर्णांकों के एक समूह को आवंटित करने और उन्हें पूर्ण संकेत देने के बजाय, आप अपने डेवलपर ज्ञान का लाभ उठा सकते हैं कि आपके पास इस विशेष एल्गोरिथ्म में कभी भी 256 से अधिक पूर्णांक नहीं होंगे। उस स्थिति में, आप पहले पूर्णांक के लिए एक संकेतक रख सकते हैं, और एक पूर्ण सूचक (4/8 बाइट्स) का उपयोग करने के बजाय चार (1 बाइट) का उपयोग करके एक सूचकांक का ट्रैक रख सकते हैं। फ्लाई पर इनमें से कुछ सूचकांकों को उत्पन्न करने के लिए आप एल्गोरिदमिक चाल का भी उपयोग कर सकते हैं।

अतीत में इस तरह की स्मृति कर्तव्यनिष्ठा बहुत लोकप्रिय थी। उदाहरण के लिए, एनईएस गेम डेटा को रटना की क्षमता पर काफी हद तक भरोसा करते हैं और सभी थोक को स्टोर करने के बजाय एल्गोरिदम को इंगित करते हैं।

चरम मेमोरी की स्थिति भी आपको ऐसा करने के लिए प्रेरित कर सकती है जैसे संकलन समय पर आपके द्वारा संचालित सभी रिक्त स्थान को आवंटित करना। फिर आपको उस मेमोरी में स्टोर करने वाले पॉइंटर को डाटा के बजाय प्रोग्राम में स्टोर करना होता है। कई स्मृति बाधाओं वाली स्थितियों में, आपके पास अलग-अलग प्रोग्राम और डेटा मेमोरी (अक्सर ROM बनाम RAM) होती है, इसलिए आप उस प्रोग्राम मेमोरी में पॉइंटर्स को पुश करने के लिए अपने एल्गोरिथ्म का उपयोग करने के तरीके को समायोजित करने में सक्षम हो सकते हैं।

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


0

कई स्थितियों में संकेत वास्तव में स्मृति को बचाते हैं। पॉइंटर्स का उपयोग करने का एक सामान्य विकल्प डेटा संरचना की एक प्रति बनाना है। डेटा संरचना की एक पूरी प्रति एक सूचक से बड़ी होगी।

एक समय महत्वपूर्ण अनुप्रयोग का एक उदाहरण एक नेटवर्क स्टैक है। एक अच्छा नेटवर्क स्टैक "शून्य प्रति" के लिए डिज़ाइन किया जाएगा - और ऐसा करने के लिए पॉइंटर्स के चतुर उपयोग की आवश्यकता होती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.