c++ - programing - सी++ 11 ने एक मानक स्मृति मॉडल पेश किया। इसका क्या मतलब है? और यह सी++ प्रोग्रामिंग को कैसे प्रभावित करेगा?




सी++ प्रोग्रामिंग इन हिंदी नोट्स pdf (4)

सी ++ 11 ने एक मानक स्मृति मॉडल पेश किया, लेकिन इसका क्या अर्थ है? और यह सी ++ प्रोग्रामिंग को कैसे प्रभावित करेगा?

यह आलेख ( गेविन क्लार्क द्वारा हर्ब सटर उद्धृत करते हैं) कहते हैं कि,

मेमोरी मॉडल का मतलब है कि सी ++ कोड में अब एक मानक लाइब्रेरी है, इस पर ध्यान दिए बिना कि किसने कंपाइलर बनाया है और यह किस प्लेटफॉर्म पर चल रहा है। यह नियंत्रित करने का एक मानक तरीका है कि विभिन्न धागे प्रोसेसर की स्मृति से कैसे बात करते हैं।

"जब आप मानक में अलग-अलग कोरों में विभाजित [कोड] के बारे में बात कर रहे हैं, तो हम मेमोरी मॉडल के बारे में बात कर रहे हैं। हम निम्नलिखित मानकों को तोड़ने के बिना इसे अनुकूलित करने जा रहे हैं, लोग कोड में शामिल होने जा रहे हैं।"

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

तो, जो मैं मूल रूप से जानना चाहता हूं वह है, सी ++ प्रोग्रामर बहु-थ्रेडेड अनुप्रयोगों को पहले भी विकसित करते थे, तो यह कैसे मायने रखता है कि यह पॉज़िक्स थ्रेड, या विंडोज थ्रेड, या सी ++ 11 धागे हैं? क्या लाभ हैं? मैं निम्न स्तर के विवरण को समझना चाहता हूं।

मुझे यह भी महसूस होता है कि सी ++ 11 मेमोरी मॉडल किसी भी तरह से सी ++ 11 बहु-थ्रेडिंग समर्थन से संबंधित है, क्योंकि मैं अक्सर इन दोनों को एक साथ देखता हूं। यदि यह है, तो वास्तव में कैसे? उन्हें क्यों संबंधित होना चाहिए?

जैसा कि मुझे नहीं पता कि बहु-थ्रेडिंग के आंतरिक कैसे काम करते हैं, और सामान्य रूप से स्मृति मॉडल का अर्थ क्या है, कृपया इन अवधारणाओं को समझने में मेरी सहायता करें। :-)


इसका मतलब है कि मानक अब बहु-थ्रेडिंग को परिभाषित करता है, और यह परिभाषित करता है कि एकाधिक धागे के संदर्भ में क्या होता है। बेशक, लोग अलग-अलग कार्यान्वयन का इस्तेमाल करते थे, लेकिन यह पूछने की तरह है कि हमारे पास std::string क्यों होनी चाहिए जब हम सभी घर-लुढ़काए string क्लास का उपयोग कर सकें।

जब आप POSIX थ्रेड या विंडोज थ्रेड के बारे में बात कर रहे हैं, तो यह एक भ्रम का थोड़ा सा है क्योंकि वास्तव में आप x86 थ्रेड के बारे में बात कर रहे हैं, क्योंकि यह एक हार्डवेयर फ़ंक्शन एक साथ चलने के लिए है। सी ++ 0 एक्स मेमोरी मॉडल गारंटी देता है, भले ही आप x86, या एआरएम, या MIPS , या कुछ भी जिसके साथ आप आ सकते हैं।


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

बहुत महत्वपूर्ण बात यह है कि लॉक (और लॉकिंग के साथ समवर्ती अर्थशास्त्र) को आम तौर पर एक क्रॉस प्लेटफ़ॉर्म तरीके से कार्यान्वित किया जाता है ... इसलिए यदि आप बिना किसी डेटा रेस वाले मल्टीथ्रेड प्रोग्राम में मानक ताले का उपयोग कर रहे हैं तो आपको क्रॉस प्लेटफ़ॉर्म मेमोरी मॉडल के बारे में चिंता करने की आवश्यकता नहीं है

दिलचस्प बात यह है कि सी ++ के लिए माइक्रोसॉफ्ट कंपाइलर्स ने अस्थिरता के लिए अर्थशास्त्र प्राप्त / रिलीज़ किया है जो सी ++ में स्मृति मॉडल की कमी से निपटने के लिए सी ++ एक्सटेंशन है http://msdn.microsoft.com/en-us/library/12a04hfd(v=vs.80).aspx । हालांकि, यह देखते हुए कि विंडोज केवल x86 / x64 पर चलता है, यह इतना नहीं कह रहा है (इंटेल और एएमडी मेमोरी मॉडल एक भाषा में अधिग्रहण / रिलीज सेमेन्टिक्स को लागू करने के लिए आसान और कुशल बनाते हैं)।


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

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

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


सबसे पहले, आपको एक भाषा वकील की तरह सोचना सीखना है।

सी ++ विनिर्देश किसी विशेष कंपाइलर, ऑपरेटिंग सिस्टम या सीपीयू का संदर्भ नहीं देता है। यह एक सार मशीन का संदर्भ देता है जो वास्तविक प्रणालियों का एक सामान्यीकरण है। भाषा वकील की दुनिया में, प्रोग्रामर का काम सार मशीन के लिए कोड लिखना है; कंपाइलर का काम उस कोड को कंक्रीट मशीन पर वास्तविक बनाना है। Specidly specidly कोडिंग करके, आप निश्चित हो सकते हैं कि आपका कोड किसी भी सिस्टम पर एक अनुपालन C ++ कंपाइलर के साथ संकलन के बिना संकलित और चलाएगा, चाहे आज या 50 साल से हो।

सी ++ 98 / सी ++ 03 विनिर्देश में सार मशीन मूल रूप से सिंगल-थ्रेडेड है। तो बहु-थ्रेडेड सी ++ कोड लिखना संभव नहीं है जो spec के संबंध में "पूरी तरह से पोर्टेबल" है। यह कल्पना मेमोरी लोड और स्टोर्स की परमाणुता के बारे में कुछ भी नहीं कहती है या जिस क्रम में लोड और स्टोर हो सकते हैं, म्यूटेक्स जैसी चीज़ों को कभी भी ध्यान न दें।

बेशक, आप विशेष कंक्रीट सिस्टम के लिए अभ्यास में बहु थ्रेडेड कोड लिख सकते हैं - जैसे कि पेंथ्रेड या विंडोज। लेकिन सी ++ 98 / सी ++ 03 के लिए बहु थ्रेडेड कोड लिखने का कोई मानक तरीका नहीं है।

सी ++ 11 में सार मशीन डिज़ाइन द्वारा बहु-थ्रेडेड है। इसमें एक अच्छी तरह से परिभाषित स्मृति मॉडल भी है ; यानी, यह कहता है कि स्मृति तक पहुंचने की बात आने पर संकलक क्या कर सकता है और नहीं कर सकता है।

निम्नलिखित उदाहरण पर विचार करें, जहां वैश्विक चर की एक जोड़ी दो धागे द्वारा समवर्ती रूप से उपयोग की जाती है:

           Global
           int x, y;

Thread 1            Thread 2
x = 17;             cout << y << " ";
y = 37;             cout << x << endl;

थ्रेड 2 आउटपुट क्या हो सकता है?

सी ++ 98 / सी ++ 03 के तहत, यह अपरिभाषित व्यवहार भी नहीं है; सवाल स्वयं व्यर्थ है क्योंकि मानक "थ्रेड" नामक किसी भी चीज पर विचार नहीं करता है।

सी ++ 11 के तहत, परिणाम अपरिभाषित व्यवहार है, क्योंकि लोड और स्टोर सामान्य रूप से परमाणु नहीं होना चाहिए। जो अधिक सुधार की तरह प्रतीत नहीं हो सकता है ... और स्वयं ही, यह नहीं है।

लेकिन सी ++ 11 के साथ, आप इसे लिख सकते हैं:

           Global
           atomic<int> x, y;

Thread 1                 Thread 2
x.store(17);             cout << y.load() << " ";
y.store(37);             cout << x.load() << endl;

अब चीजें ज्यादा दिलचस्प हो जाती हैं। सबसे पहले, यहां व्यवहार परिभाषित किया गया है । थ्रेड 2 अब 0 0 प्रिंट कर सकता है (यदि यह थ्रेड 1 से पहले चलता है), 37 17 (यदि यह थ्रेड 1 के बाद चलता है), या 0 17 (यदि यह थ्रेड 1 के बाद चलता है तो एक्स को असाइन करता है लेकिन इससे पहले कि यह वाई को असाइन करता है)।

यह मुद्रित नहीं कर सकता है 37 0 , क्योंकि सी ++ 11 में परमाणु भार / स्टोर के लिए डिफ़ॉल्ट मोड अनुक्रमिक स्थिरता को लागू करना है । इसका मतलब यह है कि सभी लोड और स्टोर्स "जैसे" हो सकते हैं, जैसा कि आपने उन्हें प्रत्येक थ्रेड के भीतर लिखा था, जबकि धागे के बीच संचालन को अंतःस्थापित किया जा सकता है, हालांकि सिस्टम पसंद करता है। तो परमाणुओं का डिफ़ॉल्ट व्यवहार भार और दुकानों के लिए परमाणुता और व्यवस्था दोनों प्रदान करता है।

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

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_relaxed);   cout << y.load(memory_order_relaxed) << " ";
y.store(37,memory_order_relaxed);   cout << x.load(memory_order_relaxed) << endl;

सीपीयू जितना आधुनिक होगा, उतना ही अधिक संभावना है कि यह पिछले उदाहरण की तुलना में तेज़ हो।

अंत में, अगर आपको केवल विशेष भार और स्टोर रखने की आवश्यकता है, तो आप लिख सकते हैं:

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_release);   cout << y.load(memory_order_acquire) << " ";
y.store(37,memory_order_release);   cout << x.load(memory_order_acquire) << endl;

यह हमें आदेशित लोड और स्टोर्स पर वापस ले जाता है - इसलिए 37 0 अब संभव आउटपुट नहीं है - लेकिन यह न्यूनतम ओवरहेड के साथ ऐसा करता है। (इस छोटे उदाहरण में, परिणाम पूर्ण उड़ा अनुक्रमिक स्थिरता के समान है; एक बड़े कार्यक्रम में, यह नहीं होगा।)

बेशक, यदि आप केवल एक ही आउटपुट देखना चाहते हैं तो 0 0 या 37 17 , तो आप मूल कोड के आस-पास एक म्यूटेक्स लपेट सकते हैं। लेकिन अगर आपने इसे अभी तक पढ़ा है, तो मैं शर्त लगाता हूं कि आप पहले ही जानते हैं कि यह कैसे काम करता है, और यह उत्तर मेरे इरादे से पहले से ही लंबा है :-)।

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

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

इस सामान पर अधिक जानकारी के लिए, इस ब्लॉग पोस्ट को देखें।





memory-model