क्या छूट का विवरण गलत तरीके से लागू करने में गलत है?


13

में के प्रलेखन std::memory_ordercppreference.com पर आराम आदेश का एक उदाहरण है:

आराम से ऑर्डर देना

टैग किए गए परमाणु संचालन memory_order_relaxedसिंक्रनाइज़ेशन ऑपरेशन नहीं हैं; वे समवर्ती मेमोरी एक्सेस के बीच एक आदेश नहीं देते हैं। वे केवल परमाणुता और संशोधन आदेश स्थिरता की गारंटी देते हैं।

उदाहरण के लिए, शुरू में x और y शून्य के साथ,

// Thread 1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
// Thread 2:
r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D

r1 == r2 == 42 का उत्पादन करने की अनुमति दी गई है, हालांकि, A को अनुक्रम से पहले B को थ्रेड 1 के भीतर और C को थ्रेड 2 के भीतर से पहले अनुक्रमित किया गया है , कुछ भी D के y के संशोधन क्रम में A के सामने आने से रोकता है, और B से X के संशोधन क्रम में C से पहले दिखाई दे रहा है। Y पर D का साइड-इफ़ेक्ट थ्रेड A में लोड A को दिखाई दे सकता है जबकि X पर B का साइड इफेक्ट लोड C को थ्रेड 2 में दिखाई दे सकता है। विशेष रूप से, यह तब हो सकता है जब D, C से पहले पूरा हो जाए। थ्रेड 2, या तो कंपाइलर रिडरिंग या रनटाइम के कारण।

यह कहता है "सी थ्रेड 2 के भीतर डी से पहले अनुक्रमित है"।

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


क्या आपका प्रश्न विशेष रूप से C ++ 11 के बारे में है?
जिज्ञासु

नहीं, यह c ++ 14,17 पर भी लागू होता है। मुझे पता है कि कंपाइलर और सीपीयू दोनों डी के साथ सी को रीबॉर्ड कर सकते हैं। यदि रीऑर्डरिंग होती है, तो डी शुरू होने से पहले सी पूरा नहीं हो सकता है। इसलिए मुझे लगता है कि वाक्य में एक शब्दावली-दुरुपयोग है "A को अनुक्रमित किया गया है- थ्रेड 1 के भीतर B और C को थ्रेड 2 के भीतर D से पहले अनुक्रमित किया गया है"। यह कहना अधिक सटीक है कि "कोड में, A को थ्रेड 1 के भीतर बी से पहले और बी को थ्रेड से डी 2 के भीतर रखा गया है"। इस सवाल का उद्देश्य इस विचार की पुष्टि करता है
Abigaile

कुछ भी नहीं "परिभाषित" के कार्यकाल में परिभाषित किया गया है।
curiousguy

जवाबों:


12

मेरा मानना ​​है कि cppreference सही है। मुझे लगता है कि यह "as-if" नियम [intro.execution] / 1 को उबालता है । कंपाइलर केवल आपके कोड द्वारा वर्णित कार्यक्रम के अवलोकन योग्य व्यवहार को पुन: उत्पन्न करने के लिए बाध्य हैं। एक अनुक्रम-पहले संबंध केवल उस धागे के परिप्रेक्ष्य से मूल्यांकन के बीच स्थापित किया जाता है जिसमें ये मूल्यांकन किए जाते हैं [intro.execution] / 15 । इसका मतलब है कि जब दो मूल्यांकन एक के बाद एक किसी न किसी धागे में कहीं दिखाई देते हैं, तो वास्तव में उस धागे में चल रहे कोड को ऐसा व्यवहार करना चाहिए जैसे कि जो भी पहले मूल्यांकन करता है वह वास्तव में जो भी दूसरा मूल्यांकन करता है उसे प्रभावित करता है। उदाहरण के लिए

int x = 0;
x = 42;
std::cout << x;

42 को प्रिंट करना चाहिए । हालाँकि, कंपाइलर को वास्तव में वैल्यू 42 को किसी ऑब्जेक्ट में स्टोर करने की ज़रूरत नहीं है, xइसे प्रिंट करने के लिए उस ऑब्जेक्ट से वैल्यू बैक पढ़ने से पहले। यह भी याद रख सकते हैं कि पिछले मूल्य को x42 में संग्रहीत किया जाना था और फिर मूल्य 42 से वास्तविक दुकान करने से पहले सीधे मूल्य 42 को प्रिंट करें x। वास्तव में, यदि xएक स्थानीय चर है, तो यह केवल ट्रैक कर सकता है कि किसी भी बिंदु पर उस चर को किस मूल्य पर सौंपा गया है और कभी भी कोई वस्तु नहीं बनाई गई है या वास्तव में मान को संग्रहीत नहीं किया गया है। थ्रेड के लिए अंतर बताने का कोई तरीका नहीं है। व्यवहार हमेशा होने जा रहा है के रूप में अगर वहाँ एक चर रहा था और जैसे कि मूल्य 42 वास्तव में एक वस्तु में संग्रहीत किया गया x से पहलेउस वस्तु से लोड किया जा रहा है। लेकिन इसका मतलब यह नहीं है कि उत्पन्न मशीन कोड वास्तव में कभी भी कहीं भी स्टोर और लोड करना है। यह आवश्यक है कि उत्पन्न मशीन कोड का अवलोकनीय व्यवहार इस बात से अप्रभेद्य है कि यदि ये सभी चीजें वास्तव में होती हैं तो व्यवहार क्या होगा।

अगर हम देखें

r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D

तब हां, C को D. से पहले अनुक्रमित किया जाता है लेकिन जब इस थ्रेड को आइसोलेशन में देखा जाता है, तो कुछ भी ऐसा नहीं होता है जो C, D. के परिणाम को प्रभावित करता है और ऐसा कुछ भी नहीं है जो D सी के परिणाम को बदल देता है। एक ही तरीका दूसरे को प्रभावित कर सकता है। एक और धागे में कुछ होने के अप्रत्यक्ष परिणाम के रूप में। हालाँकि, निर्दिष्ट करके std::memory_order_relaxed, आपने स्पष्ट रूप से कहा हैयह कि जिस क्रम में लोड और स्टोर दूसरे धागे से देखे जाते हैं वह अप्रासंगिक है। चूंकि कोई अन्य धागा किसी विशेष क्रम में लोड और स्टोर का निरीक्षण नहीं कर सकता है, इसलिए सी और डी एक दूसरे को सुसंगत तरीके से प्रभावित करने के लिए कोई अन्य धागा नहीं कर सकते हैं। इस प्रकार, जिस क्रम में लोड और स्टोर वास्तव में किया जाता है वह अप्रासंगिक है। इस प्रकार, संकलक उन्हें फिर से व्यवस्थित करने के लिए स्वतंत्र है। और, जैसा कि उस उदाहरण के नीचे बताया गया है, यदि D का स्टोर C से लोड होने से पहले किया जाता है, तो r1 == r2 == 42 वास्तव में आ सकता है ...


इसलिए अनिवार्य रूप से मानक कहते हैं कि C को D से पहले होना चाहिए , लेकिन कंपाइलर का मानना ​​है कि यह साबित नहीं किया जा सकता है कि C या D आगे हुआ और क्या, अगर-रूल के कारण, वैसे भी, उन्हें ठीक करता है?
फ्यूरिश

4
@Fureeish नंबर C को D से पहले होना चाहिए जहाँ तक वे जिस थ्रेड पर होते हैं वह बता सकते हैं। किसी अन्य संदर्भ से अवलोकन उस दृष्टि से असंगत हो सकता है।
डिडुप्लिकेटर

कोई "के रूप में अगर नियम" है
curiousguy


1
@curiousguy मानक करता है यह प्रावधान "के रूप में अगर नियम" एक फुटनोट में के लेबल एक: "यह प्रावधान कभी कभी कहा जाता है" के रूप में यदि "नियम" intro.execution
Caleth

1

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

उदाहरण के लिए मान लीजिए, कि निम्नलिखित तीन घटनाएँ हैं:

  • स्टोर 1 से p1
  • लोड करने के लिए अस्थायी
  • स्टोर 2 से पी 3

और पी 2 के लिखने के बाद और पी 3 के लिखने से पहले पी 2 के पढ़ने का स्वतंत्र रूप से आदेश दिया गया है, लेकिन कोई विशेष ऑर्डर नहीं है जिसमें पी 1 और पी 3 पार्टेक हो। P2 के साथ क्या किया जाता है इसके आधार पर, यह संकलक के लिए p1 पिछले p3 को हटाने के लिए अव्यावहारिक हो सकता है और अभी भी P2 के साथ आवश्यक शब्दार्थ को प्राप्त कर सकता है। मान लीजिए, हालांकि, कंपाइलर जानता था कि उपरोक्त कोड एक बड़े अनुक्रम का हिस्सा था:

  • स्टोर 1 से P2 [P2 के भार से पहले अनुक्रमित]
  • [ऊपर करो]
  • पी 3 में स्टोर 3 [पी 1 के लिए अन्य स्टोर के बाद अनुक्रम]

उस स्थिति में, यह निर्धारित कर सकता है कि यह उपरोक्त कोड के बाद स्टोर को p1 में फिर से व्यवस्थित कर सकता है और इसे निम्न स्टोर के साथ समेकित कर सकता है, इस प्रकार कोड जिसके परिणामस्वरूप p3 पहले p1 लिखे बिना लिखता है:

  • 1 पर सेट करें
  • P2 को स्टोर करें
  • स्टोर 2 से पी 3
  • स्टोर 3 से पी 1

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


1

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

यदि उपयोगकर्ता को क्रमिक रूप से निष्पादित होने के लिए दो बयानों की आवश्यकता है, तो सख्त मेमोरी ऑर्डर संचालन का उपयोग किया जा सकता है। सामान्य रूप से उपयोगकर्ता तब तक परवाह नहीं करते जब तक कि कार्यक्रम तार्किक रूप से सही न हो।


-7

आप जो भी सोचते हैं वह उतना ही मान्य है। मानक यह नहीं कहता है कि क्रमिक रूप से क्या निष्पादित होता है, क्या नहीं करता है, और इसे कैसे मिलाया जा सकता है

यह आपके लिए, और हर एक प्रोग्रामर के लिए, उस गड़बड़ी के शीर्ष पर लगातार शब्दार्थ बनाने के लिए, कई पीएचडी के योग्य काम है।

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