मेरे पास कुछ अंतराल धागे नियमित अंतराल (लगभग 1 किलोहाट) पर समय-महत्वपूर्ण प्रसंस्करण कर रहे हैं। प्रत्येक चक्र, श्रमिकों को एक कोर करने के लिए जागृत किया जाता है, जिनमें से प्रत्येक (औसत पर) अगले चक्र शुरू होने से पहले पूरा होना चाहिए। वे एक ही वस्तु पर काम करते हैं, जिसे कभी-कभी मुख्य धागे द्वारा संशोधित किया जा सकता है।दो परमाणुओं के साथ स्पिन-लॉक के लिए कम प्रतिबंधित मेमोरी ऑर्डरिंग
class Foo {
public:
void Modify();
void DoWork(SomeContext&);
private:
std::atomic_flag locked = ATOMIC_FLAG_INIT;
std::atomic<int> workers_busy = 0;
};
void Foo::Modify()
{
while(locked.test_and_set(std::memory_order_acquire)) ; // spin
while(workers_busy.load() != 0) ; // spin
// Modifications happen here ....
locked.clear(std::memory_order_release);
}
void Foo::DoWork(SomeContext&)
{
while(locked.test_and_set(std::memory_order_acquire)) ; // spin
++workers_busy;
locked.clear(std::memory_order_release);
// Processing happens here ....
--workers_busy;
}
:
दौड़ को रोकने के लिए है, लेकिन वस्तु अगले चक्र से पहले होने के संशोधन की अनुमति देने, मैं रिकॉर्ड करने के लिए कितने धागे अभी भी काम कर रहे एक परमाणु काउंटर के साथ एक स्पिन ताला का इस्तेमाल किया है
यह सभी शेष कार्यों को तुरंत पूरा करने की अनुमति देता है, बशर्ते कम से कम एक धागा शुरू हो गया हो, और अगले चक्र के लिए एक और कार्यकर्ता काम शुरू करने से पहले हमेशा ब्लॉक कर देगा।
atomic_flag
को "अधिग्रहण" और "रिलीज" मेमोरी ऑर्डर के साथ एक्सेस किया गया है, जैसा कि सी ++ 11 के साथ स्पिन-लॉक को लागू करने का एक स्वीकार्य तरीका प्रतीत होता है। documentation at cppreference.com के अनुसार:
memory_order_acquire
: इस स्मृति आदेश के साथ एक लोड आपरेशन प्रभावित स्मृति स्थान पर अधिग्रहण आपरेशन करता है: कोई स्मृति वर्तमान सूत्र में पहुँचता इस भार से पहले पुनर्क्रमित जा सकता है। यह सुनिश्चित करता है कि अन्य थ्रेड में सभी लिखते हैं जो समान परमाणु चर को छोड़ते हैं, वर्तमान धागे में दिखाई देते हैं।
memory_order_release
: इस स्मृति आदेश के साथ एक दुकान संचालन रिहाई आपरेशन करता है: कोई स्मृति वर्तमान सूत्र में पहुँचता इस स्टोर के बाद पुनर्क्रमित जा सकता है। यह सुनिश्चित करता है कि वर्तमान धागे में सभी लिखने वाले अन्य धागे में दिखाई दे रहे हैं जो समान परमाणु चर प्राप्त करते हैं और लिखते हैं कि परमाणु चर में निर्भरता अन्य थ्रेडों में दिखाई देती है जो समान परमाणु का उपभोग करते हैं।
जैसा कि मैं उपरोक्त को समझता हूं, यह स्मृति क्रम के बारे में अत्यधिक रूढ़िवादी होने के बिना, म्यूटेक्स व्यवहार प्रदान करने के लिए धागे में संरक्षित पहुंच को सिंक्रनाइज़ करने के लिए पर्याप्त है।
मैं क्या जानना चाहता हूं कि मेमोरी ऑर्डरिंग को और आराम दिया जा सकता है क्योंकि इस पैटर्न का दुष्प्रभाव यह है कि मैं एक अन्य परमाणु चर को सिंक्रनाइज़ करने के लिए स्पिन-लॉक म्यूटेक्स का उपयोग कर रहा हूं।
++workers_busy
पर कॉल, --workers_busy
और workers_busy.load()
वर्तमान में सभी में डिफ़ॉल्ट मेमोरी ऑर्डर, memory_order_seq_cst
है। यह देखते हुए कि इस परमाणु के लिए एकमात्र दिलचस्प उपयोग Modify()
को --workers_busy
(जो स्पिन-लॉक म्यूटेक्स द्वारा सिंक्रनाइज़ नहीं किया गया है) को अनवरोधित करना है, क्या "अधिग्रहण" वृद्धि का उपयोग करके समान चर-रिलीज मेमोरी ऑर्डर का उपयोग इस चर के साथ किया जा सकता है ? अर्थात
void Foo::Modify()
{
while(locked.test_and_set(std::memory_order_acquire)) ;
while(workers_busy.load(std::memory_order_acquire) != 0) ; // <--
// ....
locked.clear(std::memory_order_release);
}
void Foo::DoWork(SomeContext&)
{
while(locked.test_and_set(std::memory_order_acquire)) ;
workers_busy.fetch_add(1, std::memory_order_relaxed); // <--
locked.clear(std::memory_order_release);
// ....
workers_busy.fetch_sub(1, std::memory_order_release); // <--
}
यह सही है? क्या इनमें से किसी भी मेमोरी ऑर्डरिंग को आराम से आराम करना संभव है? और क्या इससे कोई फर्क पड़ता है?
अस्वीकरण: परमाणुओं पर एक विशेषज्ञ नहीं। क्या स्पिन लॉक के बाहर 'fetch_sub' कम से कम' memory_order_acq_rel' होना चाहिए ताकि यह सुनिश्चित किया जा सके कि यह अन्य धागे द्वारा गिनती पर लिखता है, _and_ कि अन्य धागे इसे लिखते हुए लिखते हैं? कुछ दिख रहा है। – ShadowRanger
आपका हार्डवेयर प्लेटफ़ॉर्म क्या है? ध्यान रखें कि सी ++ की कुछ मेमोरी ऑर्डरिंग सुविधाएं अपेक्षाकृत गूढ़ प्लेटफार्मों के लिए हैं। आप बिना किसी प्रत्यक्ष लाभ के लिए बहुत सारे काम और सीख रहे हैं! – Yakk
@Yakk: सिर्फ इसलिए कि कुछ 'memory_order' स्थिरांक द्वारा कोई विशेष हार्डवेयर विशेषताएं नहीं हैं इसका मतलब यह नहीं है कि वे कुछ भी नहीं करते हैं। यहां तक कि x86 पर (जिसने मेमोरी सेमेन्टिक्स को दृढ़ता से आदेश दिया है), 'memory_order' की पसंद संकलक अनुकूलन/पुनर्निर्देशन प्रतिबंधों को बदलती है; एक पंक्ति में दो 'memory_order_relaxed' ऑपरेशंस को कंपाइलर द्वारा स्वैप किया जा सकता है, इसलिए दूसरा ऑपरेशन पहले होता है। इसी प्रकार, 'आराम' या 'रिलीज' स्टोर की आम तौर पर शून्य ओवरहेड होती है, लेकिन 'memory_order_seq_cst' के साथ, कंपाइलर स्पष्ट, महंगा (~ 100 चक्र देरी)' mfence' निर्देश जोड़ता है। – ShadowRanger