2013-08-14 8 views
10

मैं के रूप में Herb Sutter द्वारा वर्णित लघु सी निम्नलिखित ++ कार्यक्रम झूठी बंटवारे प्रभाव पुन: पेश करने में लिखा है:कैश लाइनों, झूठी साझा करने और संरेखण

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

void thread_func(int* ptr) 
{ 
    for (unsigned i = 0; i < WORKLOAD/PARALLEL; ++i) 
    { 
     (*ptr)++; 
    } 
} 

int main() 
{ 
    int arr[PARALLEL * PADDING]; 
    thread threads[PARALLEL]; 

    for (unsigned i = 0; i < PARALLEL; ++i) 
    { 
     threads[i] = thread(thread_func, &(arr[i * PADDING])); 
    } 
    for (auto& th : threads) 
    { 
     th.join(); 
    } 
    return 0; 
} 

मुझे लगता है कि विचार समझना आसान है। आप

#define PADDING 16 

सेट करते हैं हर धागा एक अलग कैश लाइन पर काम करते हैं (एक कैश लाइन की लंबाई संभालने 64 बाइट्स होने के लिए) होगा। तो परिणाम PARALLEL> # कोर तक गति की रैखिक वृद्धि होगी। यदि, दूसरी तरफ, पैडिंग 16 से नीचे किसी भी मूल्य पर सेट है, तो किसी को गंभीर विवाद का सामना करना चाहिए, क्योंकि कम से कम दो धागे अब उसी कैश लाइन पर काम करने की संभावना रखते हैं, हालांकि एक अंतर्निहित हार्डवेयर म्यूटेक्स द्वारा संरक्षित किया जाता है। हम उम्मीद करेंगे कि हमारे स्पीडअप न केवल इस मामले में सबलाइनर होंगे, बल्कि अदृश्य लॉक काफिले की वजह से हमेशा < 1 रहेंगे।

अब, मेरे पहले प्रयासों ने इन अपेक्षाओं को लगभग संतुष्ट कर दिया है, फिर भी झूठी साझाकरण से बचने के लिए आवश्यक पैडिंग का न्यूनतम मूल्य लगभग 8 था और 16 नहीं था। जब तक मैं स्पष्ट निष्कर्ष तक नहीं आया, तब तक मैं लगभग आधे घंटे तक परेशान था, मुख्य मेमोरी के अंदर कैश लाइन की शुरुआत में मेरी सरणी को गठबंधन करने की कोई गारंटी नहीं है। सरणी के आकार सहित कई स्थितियों के आधार पर वास्तविक संरेखण भिन्न हो सकता है।

इस उदाहरण में, हमें विशेष रूप से सरणी को गठबंधन करने की आवश्यकता नहीं है, क्योंकि हम केवल 16 पर पैडिंग छोड़ सकते हैं और सब ठीक काम करता है। लेकिन कोई भी मामलों की कल्पना कर सकता है, जहां इससे कोई फर्क पड़ता है, चाहे कोई निश्चित संरचना कैश लाइन से गठबंधन हो या नहीं। इसलिए, मैंने अपनी सरणी के वास्तविक संरेखण के बारे में कुछ जानकारी प्राप्त करने के लिए कोड की कुछ पंक्तियां जोड़ दीं।

int main() 
{ 
    int arr[PARALLEL * 16]; 
    thread threads[PARALLEL]; 
    int offset = 0; 

    while (reinterpret_cast<int>(&arr[offset]) % 64) ++offset; 
    for (unsigned i = 0; i < PARALLEL; ++i) 
    { 
     threads[i] = thread(thread_func, &(arr[i * 16 + offset])); 
    } 
    for (auto& th : threads) 
    { 
     th.join(); 
    } 
    return 0; 
} 

इस समाधान के बावजूद इस मामले में मेरे लिए बाहर काम ठीक, मुझे यकीन है कि अगर यह सामान्य रूप में एक अच्छा दृष्टिकोण होगा नहीं हूँ। तो यहां मेरा प्रश्न है:

क्या उपरोक्त उदाहरण में किए गए कार्यों के अलावा अन्य कैश लाइनों के साथ गठबंधन स्मृति में ऑब्जेक्ट्स रखने का कोई आम तरीका है?

(छ का उपयोग कर ++ MinGW Win32 86 v.4.8.1 POSIX बौना Rev3)

+0

VirtualAlloc? यह पृष्ठों को खांसी देता है, इसलिए गठबंधन किया जाना चाहिए। –

+0

मुझे आश्चर्य है कि आप कोई अंतर देख रहे हैं।कंपाइलर को एक रजिस्टर के अंदर '* ptr' रखना चाहिए = जिससे झूठी साझा करने वाले जुर्माना को छुपाया जा सकता है। – Mysticial

+0

सीखने के लिए, मैंने कंपाइलर अनुकूलन को बदल दिया, इसलिए प्रत्येक बार 'ptr' को संदर्भित किया जाना चाहिए। –

उत्तर

10

आप संकलक से आवश्यक संरेखण अनुरोध करने के लिए सक्षम होना चाहिए:

alignas(64) int arr[PARALELL * PADDING]; // align the array to a 64 byte line 
+1

मैंने पहले यह कोशिश नहीं की थी, क्योंकि मैंने सोचा था कि संरेखण विनिर्देशक पर कंपाइलर का व्यवहार एबीआई पर निर्भर हो सकता है। वैसे यह पता चला कि यह अब तक परीक्षण की गई हर मशीन पर काम करता है। धन्यवाद। –

4

जीसीसी एक गठबंधन कीवर्ड का समर्थन करता है: http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html

int arr[PARALLEL * 16] __attribute__ ((aligned (8)));

:

आप शायद कुछ इस तरह चाहते हैं

यह arr को आठ-बाइट सीमा तक संरेखित करता है।

दृश्य स्टूडियो एक समान सुविधा भी है: http://msdn.microsoft.com/en-us/library/83ythb65.aspx

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