c++ - [[Carries_dependency]] विशेषता का क्या अर्थ है?




multithreading c++11 (2)

क्या कोई इसे ऐसी भाषा में समझा सकता है जो केवल प्राणियों को समझता है?


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

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

वास्तव में, ऐसा लगता है कि कुछ इंजीनियरिंग मामलों में स्मृति (और दिमाग) की कार्यक्षमता में कुछ बड़ी बग है ....> :-)


[[carries_dependency]] का प्रयोग फ़ंक्शन कॉल में निर्भरताओं को करने की अनुमति देने के लिए किया जाता है। आईबीएम के पावर आर्किटेक्चर जैसे कमजोर आदेशित आर्किटेक्चर के साथ प्लेटफॉर्म पर धागे के बीच मूल्यों को स्थानांतरित करने के लिए std::memory_order_consume साथ उपयोग किए जाने पर यह संभावित रूप से संकलक को बेहतर कोड उत्पन्न करने की अनुमति देता है।

विशेष रूप से, यदि memory_order_consume साथ पढ़ा गया मान किसी फ़ंक्शन में पास किया जाता है, तो [[carries_dependency]] बिना, तो संकलक को यह सुनिश्चित करने के लिए मेमोरी बाड़ निर्देश जारी करना पड़ सकता है कि उचित मेमोरी ऑर्डरिंग [[carries_dependency]] पालन ​​किया जाता है। यदि पैरामीटर [[carries_dependency]] साथ एनोटेट किया गया है तो संकलक यह मान सकता है कि फ़ंक्शन बॉडी सही ढंग से निर्भरता ले लेगी, और यह बाड़ अब आवश्यक नहीं हो सकती है।

इसी तरह, यदि कोई फ़ंक्शन memory_order_consume लोड किया गया मान देता है, या इस तरह के मान से व्युत्पन्न होता है, तो [[carries_dependency]] के बिना संकलक को बाड़ निर्देश डालने की आवश्यकता हो सकती है ताकि उचित मेमोरी ऑर्डरिंग सेमेन्टिक्स को बरकरार रखा जा सके। [[carries_dependency]] एनोटेशन के साथ, यह बाड़ अब आवश्यक नहीं हो सकता है, क्योंकि कॉलर अब निर्भरता पेड़ को बनाए रखने के लिए ज़िम्मेदार है।

जैसे

void print(int * val)
{
    std::cout<<*p<<std::endl;
}

void print2(int * [[carries_dependency]] val)
{
    std::cout<<*p<<std::endl;
}

std::atomic<int*> p;
int* local=p.load(std::memory_order_consume);
if(local)
    std::cout<<*local<<std::endl; // 1

if(local)
    print(local); // 2

if(local)
    print2(local); // 3

लाइन (1) में, निर्भरता स्पष्ट है, इसलिए संकलक जानता है कि local को संदर्भित किया गया है, और यह सुनिश्चित करना चाहिए कि निर्भरता श्रृंखला को पावर पर बाड़ से बचने के लिए संरक्षित किया गया हो।

लाइन (2) में, print की परिभाषा अपारदर्शी है (मान लीजिए कि यह रेखांकित नहीं है), इसलिए यह सुनिश्चित करने के लिए कि संकलक *p print में सही मान देता है, संकलक को बाड़ जारी करनी चाहिए।

लाइन (3) पर, संकलक यह मान सकता है कि हालांकि print2 भी अपारदर्शी है, फिर पैरामीटर से निर्भरता मान पर निर्भरता निर्देश धारा में संरक्षित है, और पावर पर कोई बाड़ आवश्यक नहीं है। जाहिर है, print2 की परिभाषा वास्तव में इस निर्भरता को संरक्षित print2 चाहिए, इसलिए विशेषता print2 के लिए जेनरेट कोड को भी प्रभावित print2





carries-dependency