2013-02-13 59 views
30

किसी भी std::atomic<T> जहां टी एक आदिम प्रकार है:मोल/रिलीज बनाम क्रमिक रूप से लगातार स्मृति आदेश

अगर मैं std::memory_order_acq_relfetch_xxx के लिए संचालन का उपयोग करें, और load ऑपरेशन के लिए std::memory_order_acquire और store आपरेशन आँख बंद करके के लिए std::memory_order_release (मैं तो बस रीसेट करने की तरह मतलब उन कार्यों का डिफ़ॉल्ट स्मृति आदेश)

  • परिणाम के रूप में अगर मैं std::memory_order_seq_cst इस्तेमाल किया (जो डिफ़ॉल्ट रूप में इस्तेमाल किया जा रहा है) की घोषणा की संचालन से किसी के लिए ही होगा?
  • यदि परिणाम समान थे, तो क्या यह उपयोग दक्षता के मामले में std::memory_order_seq_cst का उपयोग करने से अलग है?
+0

यह क्या अंतर्निहित हार्डवेयर के लिए प्रस्ताव दिया पर निर्भर करता है। यदि आप विशेष रूप से नहीं जानते कि यह कैसे काम करता है, और उसके अनुसार अनुकूलित करने के लिए मजबूर किया जाता है, तो डिफ़ॉल्ट शायद ठीक है। सामान्य x86 सिस्टम पर यदि कोई हो, तो बहुत कम अंतर होगा। X12 पर –

+1

@ बो पर्सन, जीसीसी एक seq_cst स्टोर के बाद एक पूर्ण MFENCE डालता है। यह आपको – LWimsey

उत्तर

56

परमाणु संचालन के लिए सी ++ 11 मेमोरी ऑर्डरिंग पैरामीटर ऑर्डरिंग पर बाधा निर्दिष्ट करते हैं।यदि आप std::memory_order_release के साथ एक स्टोर करते हैं, और किसी अन्य थ्रेड से लोड std::memory_order_acquire के साथ मान पढ़ता है तो दूसरे थ्रेड से बाद के पढ़ने वाले ऑपरेशन स्टोर के रिलीज से पहले पहले थ्रेड द्वारा किसी भी स्मृति स्थान पर संग्रहीत किसी भी मान को देखेंगे, या बाद में स्टोर में से किसी भी स्मृति स्थान

यदि दोनों स्टोर और बाद के लोड std::memory_order_seq_cst हैं तो इन दो धागे के बीच संबंध समान है। अंतर देखने के लिए आपको एक और धागे की आवश्यकता है।

उदा std::atomic<int> चर x और y, दोनों शुरू में 0.

थ्रेड 1:

x.store(1,std::memory_order_release); 

थ्रेड 2:

y.store(1,std::memory_order_release); 

थ्रेड 3:

int a=x.load(std::memory_order_acquire); // x before y 
int b=y.load(std::memory_order_acquire); 

थ्रेड 4:

int c=y.load(std::memory_order_acquire); // y before x 
int d=x.load(std::memory_order_acquire); 

लिखा हैं, वहाँ x और y के भंडार के बीच कोई रिश्ता है, इसलिए यह धागा 4.

में a==1, b==0 धागा 3 में, और c==1 और d==0 देखने के लिए तो सभी स्मृति बहुत संभव है orderings std::memory_order_seq_cst में बदल दिया जाता है तो इस x और y को दुकानों के बीच एक आदेश लागू करता है। नतीजतन, अगर धागा 3 a==1 और b==0 देखता है तो इसका मतलब है कि x को दुकान y को दुकान से पहले होना चाहिए, इसलिए यदि धागा 4 c==1, y को दुकान अर्थ देखता है पूरा कर लिया है, तो x को स्टोर भी पूरा कर लिया है चाहिए, ताकि हमारे पास d==1 होना चाहिए।

अभ्यास में, फिर std::memory_order_seq_cst का उपयोग करके आपके कंपाइलर और प्रोसेसर आर्किटेक्चर के आधार पर लोड या स्टोर या दोनों में अतिरिक्त ओवरहेड जोड़ दिया जाएगा। जैसे x86 प्रोसेसर के लिए एक आम तकनीक MOVstd::memory_order_seq_cst स्टोर्स के लिए निर्देशों को आवश्यक आदेश गारंटी प्रदान करने के लिए के लिए निर्देशों का उपयोग करना है, जबकि MOV पर्याप्त होगा। अधिक आराम से स्मृति आर्किटेक्चर वाले सिस्टम पर ओवरहेड अधिक हो सकता है, क्योंकि सादा भार और दुकानों की कम गारंटी होती है।

मेमोरी आदेश कठिन है। मैंने my book में लगभग एक संपूर्ण अध्याय समर्पित किया।

+0

मैं विफलता के उदाहरण की प्रतीक्षा कर रहा था, उत्तर के लिए धन्यवाद। – zahir

+0

"सभी स्मृति orderings एसटीडी :: memory_order_seq_cst बदल रहे हैं तो यह x और y के लिए दुकानों के बीच एक आदेश को लागू करता है" - यह केवल orderings में से कुछ seq_cst को सेट करके एक ही प्रभाव को प्राप्त करने के लिए संभव है? वाई की तरहस्टोर – qble

+2

नहीं। "एकल कुल आदेश" बाधा केवल 'memory_order_seq_cst' संचालन पर लागू होती है। अन्य स्मृति आदेशों के साथ संचालन शामिल नहीं हैं, और इसलिए अलग-अलग धागे में विभिन्न आदेशों में दिखाई दे सकते हैं, बशर्ते कोई अन्य बाधाएं संतुष्ट हों। –

6

मेमोरी ऑर्डरिंग काफी मुश्किल हो सकती है, और इसे गलत होने के प्रभाव अक्सर बहुत सूक्ष्म होते हैं।

सभी मेमोरी ऑर्डरिंग के साथ मुख्य बिंदु यह है कि यह गारंटी देता है कि "क्या हुआ है", ऐसा नहीं होने वाला है। उदाहरण के लिए, यदि आप कुछ चर (जैसे x = 7; y = 11;) पर कुछ संग्रहीत करते हैं, तो x में 7 मान को देखने से पहले एक अन्य प्रोसेसर y को 11 के रूप में देख सकता है। x सेटिंग और y सेट करने के बीच मेमोरी ऑर्डरिंग ऑपरेशन का उपयोग करके, आप जिस प्रोसेसर का उपयोग कर रहे हैं, वह गारंटी देगा कि x = 7;y में कुछ स्टोर करने से पहले स्मृति में लिखा गया है।

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

buffer[index] = 32; 
index = (index + 1) % buffersize; 

और कुछ अन्य धागा index उपयोग कर रहा है निर्धारित करने के लिए है कि नए मूल्य में लिखा गया है, तो हम 32 लिखा है करने के लिए की जरूरत है FIRST, फिर index अपडेट किया गया। अन्यथा, अन्य धागे old डेटा प्राप्त कर सकते हैं।

वही सैफफोर्स, म्यूटेक्स और ऐसी चीजों को काम करने के लिए लागू होता है - यही कारण है कि शब्द रिलीज और अधिग्रहण स्मृति बाधा प्रकारों के लिए उपयोग किया जाता है।

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

इससे कितना अंतर आता है? यह सिस्टम आर्चीचर क्या है इस पर अत्यधिक निर्भर है। कुछ प्रणालियों पर, कैश को [आंशिक रूप से] फ्लश करने की आवश्यकता होती है और एक कोर से दूसरे में भेजे गए इंटरप्ट्स कहते हैं, "कृपया जारी रखने से पहले कृपया इस कैश-फ्लशिंग काम को करें" - इसमें कई सौ चक्र लग सकते हैं। अन्य प्रोसेसर पर, नियमित स्मृति लिखने की तुलना में यह केवल कुछ छोटा प्रतिशत धीमा है। X86 इस तेजी से करने में बहुत अच्छा है। कुछ प्रकार के एम्बेडेड प्रोसेसर, (कुछ मॉडलों - निश्चित नहीं हैं?) उदाहरण के लिए एआरएम, सबकुछ काम करने के लिए प्रोसेसर में थोड़ा और काम करने की आवश्यकता है।

+0

को धीमा कर देता है जब आपने कहा "स्मृति में बाहर जाता है", हमें सीपीयू कैश से केंद्रीय स्मृति में समझना होगा? – Guillaume07