2008-09-16 22 views
14

64-बिट लिखने पर परमाणु होने की गारंटी कब दी जा सकती है, जब इंटेल x86- आधारित प्लेटफ़ॉर्म पर सी में प्रोग्रामिंग (विशेष रूप से, इंटेल-आधारित मैक इंटेल कंपाइलर का उपयोग कर मैकोज़क्स 10.4 चला रहा है)? उदाहरण के लिए:64-बिट लिखने की गारंटी कैसे परमाणु हैं?

unsigned long long int y; 
y = 0xfedcba87654321ULL; 
/* ... a bunch of other time-consuming stuff happens... */ 
y = 0x12345678abcdefULL; 

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

उत्तर

2

जीसीसी परमाणु संचालन के लिए अंतर्निहित है; मुझे संदेह है कि आप अन्य कंपाइलरों के साथ भी ऐसा ही कर सकते हैं। परमाणु संचालन के लिए संकलक पर भरोसा न करें; ऑप्टिमाइज़ेशन निश्चित रूप से गैर-परमाणुओं में स्पष्ट रूप से परमाणु संचालन करने का जोखिम निश्चित रूप से तब तक चलाएगा जबतक कि आप स्पष्ट रूप से ऐसा करने के लिए संकलक को नहीं बताते।

+1

आप जीसीसी इंट्रिनिक्स का उपयोग करने का सुझाव देते हैं, फिर कंपाइलर पर भरोसा न करें। क्या आप इंट्रिनिक्स के अलावा किसी अन्य चीज का जिक्र कर रहे हैं जिसे संकलक पर भरोसा नहीं किया जाना चाहिए? – Jeff

12

x86_64 पर, इंटेल कंपाइलर और जीसीसी दोनों आंतरिक आंतरिक परमाणु-संचालन कार्यों का समर्थन करते हैं। यहां उनके बारे में जीसीसी का दस्तावेज है: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html

इंटेल कंपाइलर दस्तावेज़ यहां उनके बारे में भी बात करते हैं: http://softwarecommunity.intel.com/isn/downloads/softwareproducts/pdfs/347603.pdf (पृष्ठ 164 या वहां)।

40

आपकी सबसे अच्छी शर्त प्राइमेटिव्स से अपने सिस्टम को बनाने की कोशिश करने से बचने के लिए है, और इसके बजाय लॉकिंग का उपयोग करें जब तक यह वास्तव में प्रोफाइलिंग के दौरान हॉट स्पॉट के रूप में दिखाई देता है। (यदि आपको लगता है कि आप चालाक हो सकते हैं और ताले से बच सकते हैं, तो नहीं। आप नहीं हैं। यह सामान्य "आप" है जिसमें मुझे और हर कोई शामिल है।) आपको कम से कम स्पिन लॉक का उपयोग करना चाहिए, spinlock(3) देखें। और जो भी आप करते हैं, "अपने स्वयं के" ताले को लागू करने का प्रयास न करें। आपको यह गलत लगेगा।

आखिरकार, आपको अपने ऑपरेटिंग सिस्टम द्वारा प्रदान किए जाने वाले लॉकिंग या परमाणु संचालन का उपयोग करने की आवश्यकता है। चीजों के इन प्रकार बिल्कुल सहीमें सभी मामलों हो रही बेहद मुश्किल है। अक्सर इसमें विशिष्ट प्रोसेसर के विशिष्ट संस्करणों के लिए इरेटा जैसी चीजों का ज्ञान शामिल हो सकता है। ("ओह, उस प्रोसेसर के संस्करण 2.0 ने सही समय पर कैश-कोहेन्सी स्नूपिंग नहीं किया है, यह संस्करण 2.0.1 में तय है लेकिन 2.0 पर आपको NOP डालने की आवश्यकता है।") बस एक चर पर volatile कीवर्ड डालना सी में लगभग हमेशा अपर्याप्त है।

मैक ओएस एक्स पर, इसका मतलब है कि आपको atomic(3) में सूचीबद्ध कार्यों का उपयोग 32-बिट, 64-बिट और पॉइंटर-आकार की मात्राओं पर वास्तव में परमाणु-से-ऑल-सीपीयू संचालन करने के लिए करना होगा। (पॉइंटर्स पर किसी भी परमाणु संचालन के लिए उत्तरार्द्ध का उपयोग करें ताकि आप स्वचालित रूप से 32/64-बिट संगत हो।) यह जाता है कि आप परमाणु तुलना-और-स्वैप, वृद्धि/कमी, स्पिन लॉकिंग, या स्टैक/कतार जैसी चीजें करना चाहते हैं या नहीं प्रबंधन। सौभाग्य से spinlock(3), atomic(3), और barrier(3) कार्यों सभी CPUs पर सही ढंग से सब काम है कि मैक ओएस एक्स

+7

भविष्य के 'व्यावहारिक' लॉक मुक्त सुसमाचार भेजने के लिए इस तरह के एक गर्म और अस्पष्ट जगह के लिए धन्यवाद :) + 10 अगर मैं कर सकता था। –

9

द्वारा समर्थित हैं इंटेल की processor manuals, quadword पहुंच की Part 3A - System Programming Guide के अध्याय 7 के अनुसार atomically किया जाएगा चाहिए, अगर एक 64 पर गठबंधन एक पेंटियम या नए पर सीमा सीमा, और एक पी 6 या नए पर unaligned (यदि अभी भी एक कैश लाइन के भीतर)। आपको यह सुनिश्चित करने के लिए volatile का उपयोग करना चाहिए कि संकलक एक चर में लिखने को कैश करने की कोशिश नहीं करता है, और यह सुनिश्चित करने के लिए कि उचित क्रम में लेखन होता है, आपको स्मृति बाड़ दिनचर्या का उपयोग करने की आवश्यकता हो सकती है।

यदि आपको मौजूदा मान पर लिखे गए मान को आधारभूत करने की आवश्यकता है, तो आपको अपने ऑपरेटिंग सिस्टम की इंटरलाक्ड सुविधाओं का उपयोग करना चाहिए (उदा। विंडोज इंटरलाक्डइनक्रिएट 64)।

+2

और भी विशिष्ट होने के लिए, यह पृष्ठ 325 पर §8.8.1 में बताया गया है। –

+0

यदि आप परमाणुओं के लिए सही इंटरफेस का सही उपयोग करते हैं, तो आपको 'अस्थिर' की आवश्यकता नहीं है। – Jeff

2

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

"मेमोरी बाड़" या "मेमोरी बाrier" वे शब्द हैं जिन्हें आप शोध करना चाहते हैं।

+0

'mfence' एक निर्माता-उपभोक्ता कतार के लिए अधिक है। आपको उपभोक्ता पक्ष पर उत्पादक पक्ष और 'lfence' पर केवल' sfence' की आवश्यकता है। "मेमोरी बाधाओं को हानिकारक माना जाता है" नामक एक लेख नहीं है, लेकिन वहां होना चाहिए :-) – Jeff

10

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

#include <libkern/OSAtomic.h> 
// bool OSAtomicCompareAndSwap64Barrier(int64_t oldValue, int64_t newValue, int64_t *theValue); 

void AtomicSet(uint64_t *target, uint64_t new_value) 
{ 
    while (true) 
    { 
     uint64_t old_value = *target; 
     if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, target)) return; 
    } 
} 

uint64_t AtomicGet(uint64_t *target) 
{ 
    while (true) 
    { 
     int64 value = *target; 
     if (OSAtomicCompareAndSwap64Barrier(value, value, target)) return value; 
    } 
} 

ध्यान दें कि एप्पल के OSAtomicCompareAndSwap कार्यों atomically कार्रवाई करने:

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

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

मैंने अपने कंपाइलर के खिलाफ यह जांच नहीं की है, इसलिए कृपया किसी भी टाइपो से क्षमा करें।

आपने विशेष रूप से ओएसएक्स का उल्लेख किया है, लेकिन यदि आपको अन्य प्लेटफ़ॉर्म पर काम करने की आवश्यकता है, तो विंडोज़ में कई इंटरलॉक * फ़ंक्शंस हैं, और आप उनके लिए एमएसडीएन दस्तावेज़ खोज सकते हैं। उनमें से कुछ विंडोज 2000 प्रो और बाद में काम करते हैं, और कुछ (विशेष रूप से 64-बिट कार्यों में से कुछ) Vista के साथ नए हैं। अन्य प्लेटफार्मों पर, जीसीसी संस्करण 4.1 और बाद में __sync_fetch_and_add() जैसे विभिन्न प्रकार के __sync * फ़ंक्शन हैं। अन्य प्रणालियों के लिए, आपको असेंबली का उपयोग करने की आवश्यकता हो सकती है, और आप एसईवीएन ब्राउजर में एसआईवीएनओएस प्रोजेक्ट के लिए कुछ कार्यान्वयन, src/system/libroot/os/arch के अंदर पा सकते हैं।

+0

पढ़ने के लिए आप बहुत सरल दृष्टिकोण 'ओएसएटॉमिकएड 64 बैरियर (0, लक्ष्य)' का उपयोग कर सकते हैं, यह परमाणु रूप से लक्ष्य और रिटर्न द्वारा परिवर्तनीय बिंदु पर 0 जोड़ता है इसके परिणामस्वरूप, इस मामले में * लक्ष्य स्वयं –

6

एक्स 86 पर, परमाणु रूप से एक गठबंधन 64-बिट मान लिखने का सबसे तेज़ तरीका FISTP का उपयोग करना है। असाइन किए गए मानों के लिए, आपको एक सीएएस 2 (_InterlockedExchange64) का उपयोग करने की आवश्यकता है। CAS2 ऑपरेशन BUSLOCK के कारण काफी धीमा है, हालांकि यह संरेखण की जांच करने के लिए अक्सर तेज़ हो सकता है और गठबंधन पते के लिए FISTP संस्करण कर सकता है। दरअसल, इस प्रकार Intel Threaded building Blocks परमाणु 64-बिट लिखता है।

+1

प्रति https://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32 -आर्कटेक्चर, सीएमपीएक्सएचजी ने इंटेल पेंटियम प्रो प्रोसेसर के बाद से बस लॉक को नहीं बताया है। – Jeff

1

आईएसओ सी (सी 11) का नवीनतम संस्करण atomic_store(_explicit) सहित परमाणु संचालन के एक सेट को परिभाषित करता है। उदाहरण देखें अधिक जानकारी के लिए this page

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

सीओ 2 परमाणुओं का एक स्पष्ट लाभ - पूरे आईएसओ मानक चीज़ के अलावा - यह है कि वे एक और सटीक स्मृति स्थिरता पर्चे का समर्थन करते हैं। जहां तक ​​मुझे पता है, जीसीसी परमाणु एक पूर्ण स्मृति बाधा दर्शाते हैं।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^