एक व्यावहारिक बहु-शब्द तुलना और स्वैप ऑपरेशन


10

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

word_t RDCSS(RDCSSDescriptor_t *d) {
  do {
    r = CAS1(d->a2, d->o2, d);
    if (IsDescriptor(r)) Complete(r);
  } while (IsDescriptor(r));
  if (r == d->o2) Complete(d); // !!
  return r;
}

void Complete(RDCSSDescriptor_t *d) {
  v = *(d->a1);
  if (v == d->o1) CAS1(d->a2, d, d->n2);
  else CAS1(d->a2, d, d->o2);
}

RDCSSDescriptor_tनिम्नलिखित क्षेत्रों के साथ एक संरचना कहां है:

  • a1 - पहली शर्त का पता
  • o1 - पहले पते पर अपेक्षित मूल्य
  • a2 - दूसरी स्थिति का पता
  • o2 - दूसरे पते पर अपेक्षित मूल्य
  • n2 - दूसरे पते पर लिखा जाने वाला नया मान

यह वर्णनकर्ता आरडीसीएस ऑपरेशन शुरू करने वाले धागे में एक बार बनाया और शुरू किया जाता है - फ़ंक्शन में पहला CAS1 RDCSSसफल होने तक किसी अन्य धागे का कोई संदर्भ नहीं होता है, जिससे वर्णनकर्ता पहुंच में नहीं आता (या कागज की शब्दावली में सक्रिय होता है)।

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

लेखक यह नहीं समझाते हैं कि !!टिप्पणी के साथ लाइन कागज के भीतर क्यों आवश्यक है। यह मुझे लगता है कि फ़ंक्शन CAS1में निर्देश Completeइस चेक के बाद हमेशा विफल रहेंगे, बशर्ते कि कोई समवर्ती संशोधन न हो। और अगर चेक और CAS के बीच एक समवर्ती संशोधन था Complete, तो चेक करने वाला धागा अभी भी अपनी CAS के साथ विफल होना चाहिए Complete, क्योंकि समवर्ती संशोधन में एक ही विवरणक का उपयोग नहीं करना चाहिए d

मेरा प्रश्न है: समारोह में जाँच कर सकते हैं RDCSSS, if (r == d->o2)..., RDCSS अभी भी एक डबल तुलना, एकल स्वैप अनुदेश जो है के शब्दों को बनाए रखने के साथ छोड़ देना linearizable और ताला मुक्त ? ( !!टिप्पणी के साथ लाइन )

यदि नहीं, तो क्या आप उस परिदृश्य का वर्णन कर सकते हैं जहां यह रेखा वास्तव में शुद्धता सुनिश्चित करने के लिए आवश्यक है?

धन्यवाद।


सबसे पहले, यह समझने के लिए कि क्या चल रहा है, हमें डेटा संरचना RDCSSDescriptor_t देखना होगा। दूसरे, यह संभवतः विषय से हटकर है क्योंकि यह सैद्धांतिक कंप्यूटर विज्ञान से संबंधित नहीं है; इस पर stackoverflow.com पर पूछना बेहतर होगा।
डेव क्लार्क

कागज का लिंक टूट गया है।
हारून स्टर्लिंग

1
मैं लिंक के लिए माफी माँगता हूँ - यह अब काम करना चाहिए। मैंने वर्णन करने के लिए प्रश्न को अद्यतन किया है कि विवरणक क्या है। इसका कारण मैंने इसे stackoverflow.com पर पोस्ट नहीं किया है, यह है कि FAQ यह कहता है कि यह साइट कंप्यूटर विज्ञान में अनुसंधान-स्तरीय प्रश्नों के लिए है। मैंने सोचा था कि एक एल्गोरिथ्म के लॉक-फ्रीडम और लीनियरिज़ेबिलिटी के प्रश्न इस तरह से योग्य हैं। मुझे आशा है कि मैंने अक्सर गलत तरीके से एफएक्यू को समझा।
axel22

FAQ में आपके द्वारा याद किया गया महत्वपूर्ण शब्द "सैद्धांतिक" था। जैसा कि कुछ लोगों को सवाल दिलचस्प लगता है, मैं इसे खुला छोड़ दूंगा।
डेव क्लार्क

3
@ क्या: मैं इस उप-क्षेत्र का विशेषज्ञ नहीं हूं, लेकिन मेरे लिए यह एक बहुत ही विशिष्ट TCS प्रश्न है। आपको कम्प्यूटेशन के दो मॉडल दिए गए हैं (A: सिंगल-वर्ड CAS, B: मल्टी-वर्ड CAS के साथ) और एक जटिलता माप (CAS की संख्या), और आपसे पूछा जाता है कि क्या आप मॉडल A में मॉडल B का अनुकरण कर सकते हैं, और क्या सबसे खराब स्थिति के साथ। (यहां यह थोड़ा भ्रामक हो सकता है कि सिमुलेशन को स्यूडोकोड के बजाय सी कोड के एक टुकड़े के रूप में दिया गया है; यह एक सिद्धांत व्यक्ति को सुझाव दे सकता है कि यह वास्तविक दुनिया की प्रोग्रामिंग चुनौतियों से संबंधित है।)
जुका सुकोमेला

जवाबों:


9

समवर्ती रनटाइम वातावरण में साधारण चीजें अजीब लग सकती हैं ... उम्मीद है कि यह मदद कर सकता है।

हमारे पास इस शब्दार्थ में एक BUILT-IN ATOMIC CAS1 है :

int CAS1(int *addr, int oldval, int newval) {
  int currval = *addr;
  if (currval == oldval) *addr = newval;
  return currval;
}

हमें CAS1 का उपयोग करके और निम्नलिखित अर्थ होने पर एक एटोमिक RDCSS फ़ंक्शन को परिभाषित करने की आवश्यकता है :

int RDCSS(int *addr1, int oldval1, int *addr2, int oldval2, int newval2) {
  int res = *addr;
  if (res == oldval2 && *addr1 == oldval1) *addr2 = newval2;
  return res;
}

सहज रूप से: हमें निश्चित रूप से केवल addr2 पर मान बदलने की आवश्यकता है यदि * addr1 == oldval1 ... यदि कोई अन्य थ्रेड इसे बदल रहा है तो हम ऑपरेशन को पूरा करने के लिए दूसरे थ्रेड की मदद कर सकते हैं, फिर हम पुनः प्रयास कर सकते हैं।

CASD को परिभाषित करने के लिए RDCSS फ़ंक्शन का उपयोग किया जाएगा (लेख देखें)। अब, हम निम्नलिखित तरीके से एक RDCSS डिस्क्रिप्टर को परिभाषित करते हैं :

RDCSSDESCRI
int *addr1   
int oldval1
int *addr2   
int oldval2
int newval2

फिर हम निम्नलिखित तरीके से आरडीसीएसएस लागू करते हैं:

int RDCSS( RDCSSDESCRI *d ) {
  do {
    res = CAS1(d->addr2, d->oldval2, d);  // STEP1
    if (IsDescriptor(res)) Complete(res); // STEP2
  } while (IsDescriptor(res);             // STEP3
  if (res == d->oldval2) Complete(d);     // STEP4
  return res;
}

void Complete( RDCSSDESCRI *d ) {
  int val = *(d->addr1);
  if (val == d->oldval1) CAS1(d->addr2, d, d->newval2);
    else CAS1(d->addr2, d, d->oldval2);  
}
  • STEP1: सबसे पहले हम अपने (खुद के) डिस्क्रिप्टर d के लिए * addr2 के मान को बदलने की कोशिश करते हैं, यदि CAS1 सफल होता है तो res == d-> oldval2 (यानी रेस एक डिस्क्रिप्टर नहीं है)
  • STEP2: यदि Res एक डिस्क्रिप्टर है, तो जाँच करें कि STEP1 विफल (एक और धागा addr2 बदल गया) ... ऑपरेशन पूरा करने में एक और धागा मदद करें
  • STEP3: STEP1 को फिर से लें अगर हम अपने डिस्क्रिप्टर को स्टोर करने में सफल नहीं हुए
  • STEP4: अगर हमने Addr2 से अपना अपेक्षित मूल्य प्राप्त किया है तो हम Addr2 में अपने डिस्क्रिप्टर (पॉइंटर) को संग्रहीत करने में सफल रहे और हम newval2 को * addr2 iif * addr1 = - oldval1 में संग्रहीत करते हुए अपना कार्य पूरा कर सकते हैं।

आपके सवाल का जवाब

यदि हम STEP4 को छोड़ देते हैं तो यदि (... && * addr1 == oldval1) * addr2 = newval2 RDCSS सिमेंटिक के भाग को कभी निष्पादित नहीं किया जाएगा (... या बेहतर: इसे अन्य थ्रेड्स द्वारा एक umpredictable तरीके से निष्पादित किया जा सकता है। वर्तमान एक)।

जैसा कि आपकी टिप्पणी में आपके द्वारा बताया गया है कि अगर STEP4 पर (res == d1-> oldval2) स्थिति अनावश्यक है: भले ही हम इसे छोड़ दें, दोनों CAS1 को पूर्ण () में विफल कर देंगे क्योंकि * (d-> addr2)! = D । इसके केवल purpouse एक फ़ंक्शन कॉल से बचना है।

उदाहरण टी 1 = थ्रेड 1, टी 2 = थ्रेड 2:

remember that addr1 / addr2 are in a shared data zone !!!

T1 enter RDCSS function
T2 enter RDCSS function
T2 complete STEP1 (and store the pointer to its descriptor d2 in addr2)
T1 at STEP1 the CAS1 fails and res = d2
T2 or T1 completes *(d2->addr2)=d2->newval2 (suppose that *(d2->addr1)==d2->oldval1)
T1 execute STEP1 and now CAS1 can fail because *addr2 == d2->newval2
   and maybe d2->newval2 != d1->oldval2, in every case at the end 
   res == d2->newval2 (fail) or
   res == d1->oldval2 (success)
T1 at STEP2 skips the call to Complete() (because now res is not a descriptor)
T1 at STEP3 exits the loop (because now res is not a descriptor)
T1 at STEP4 T1 is ready to store d1->newval2 to addr2, but only if
   *(d1->addr2)==d (we are working on our descriptor) and *(d1->addr1)==d1->oldval1
   ( Custom() function)

धन्यवाद, अच्छी व्याख्या। मैं पूरी तरह से इस बिंदु से चूक गया कि CAS1 पुराने मूल्य को लौटाता है, नया नहीं।
axel22

लेकिन, परिदृश्य में, अंतिम 2 पंक्तियाँ कहती हैं: STEP4 पर स्थिति के बिना, T1 मान को संग्रहीत कर सकता है, क्योंकि addr2इसमें शामिल है d2->newval2। लेकिन, यह मुझे लगता है कि वसीयत में CAS1 Completeसिर्फ असफल हो जाएगा, क्योंकि यह पुराने मूल्य को वर्णनकर्ता होने की उम्मीद करता है d1- टी 1 द्वारा कुछ भी नहीं लिखा जाएगा। सही?
axel22

@ axel22: मैं CAS1 को पूरा करने से चूक गया () :-D। हाँ, आप सही हैं ... मेरा उदाहरण गलत है, अगर किसी फ़ंक्शन कॉल से बचने के लिए केवल शर्त का उपयोग किया जाता है, अगर हम दूर फेंक देते हैं अगर () कुछ भी नहीं बदलता है। स्पष्ट रूप से STEP4 पर पूर्ण (d) आवश्यक है। अब मैं उदाहरण को संशोधित करता हूं।
मार्जियो डी बियासी

कैस से बचने के लिए हम असफल होने की उम्मीद करते हैं, जहां तक ​​मुझे पता है कि कैश ऑप्टिमाइज़ेशन तकनीक है, क्योंकि वास्तविक हार्डवेयर पर आमतौर पर कैश लाइनों को फ्लश करने और कैश लाइन तक अनन्य पहुंच प्राप्त करने जैसे नकारात्मक प्रभाव होते हैं। मुझे लगता है कि कागज के लेखक चाहते थे कि एल्गोरिथ्म सही होने के अलावा यथासंभव व्यावहारिक हो।
टिम सेगिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.