2011-01-13 8 views
6

अगर मैं कुछ कोड है कि तरह दिखता है:के बाद संकेत दिए गए

void check_flag(foo_t* f) { 
    bool cache = f->flag; 
    while(cache) 
     pthread_cond_wait(&f->c, &f->m); 
} 

अन्य में:

typedef struct { 
    bool some_flag; 

    pthread_cond_t c; 
    pthread_mutex_t m; 
} foo_t; 

// I assume the mutex has already been locked, and will be unlocked 
// some time after this function returns. For clarity. Definitely not 
// out of laziness ;) 
void check_flag(foo_t* f) { 
    while(f->flag) 
     pthread_cond_wait(&f->c, &f->m); 
} 

वहाँ सी मानक में कुछ भी रूप में check_flag पुनर्लेखन से एक अनुकूलक रोक रहा है शब्द, क्या उत्पन्न कोड है जो लूप के माध्यम से हर बार f सूचक का पालन करने के लिए है, या संकलन को खींचने के लिए संकलक मुक्त है?

यदि यह है इसे खींचने के लिए स्वतंत्र है, तो इसे रोकने के लिए कोई तरीका है? क्या मुझे कहीं अस्थिर कीवर्ड छिड़कने की ज़रूरत है? यह check_flag पैरामीटर नहीं हो सकता है क्योंकि मैं इस संरचना में अन्य चर रखने की योजना बना रहा हूं कि मुझे इस तरह के संकलक को अनुकूलित करने पर कोई फर्क नहीं पड़ता।

मैं का सहारा लेना पड़ सकता है:

void check_flag(foo_t* f) { 
    volatile bool* cache = &f->some_flag; 
    while(*cache) 
     pthread_cond_wait(&f->c, &f->m); 
} 
+0

+1 इस तरह के मुद्दे के बारे में सोचने के लिए परीक्षण और त्रुटि से थ्रेडेड कोड लिखने से पहले! –

उत्तर

3

आम तौर पर, आप म्युटेक्स pthread_cond_wait कॉल रिलीज के रूप में हालत वस्तु पर इंतजार कर से पहले pthread म्युटेक्स लॉक करने के लिए (लौटने से पहले यह पुनः प्राप्त और) कोशिश करनी चाहिए। तो, आपके check_flag फ़ंक्शन को उस तरह लिखा जाना चाहिए ताकि पठित स्थिति पर अर्थपूर्ण के अनुरूप हो।

void check_flag(foo_t* f) { 
    pthread_mutex_lock(&f->m); 
    while(f->flag) 
     pthread_cond_wait(&f->c, &f->m); 
    pthread_mutex_unlock(&f->m); 
} 

किया जाए या नहीं संकलक के सवाल के संबंध में flag क्षेत्र के पढ़ने का अनुकूलन करने की अनुमति दी है, की तुलना में मैं कर सकते हैं इस answer और अधिक विस्तार में यह बताते हैं।

असल में, कंपाइलर pthread_cond_wait, pthread_mutex_lock और pthread_mutex_unlock के अर्थशास्त्र के बारे में जानता है। वह जानता है कि वह उन परिस्थितियों में स्मृति पढ़ने को अनुकूलित नहीं कर सकता है (इस उदाहरण में pthread_cond_wait पर कॉल करें)। मेमोरी बाधा की कोई धारणा नहीं है, केवल कुछ फ़ंक्शन का एक विशेष ज्ञान है, और कुछ नियम उनकी उपस्थिति में पालन करते हैं।

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

+0

क्या इसका मतलब यह है कि संकलक एक रजिस्टर में 'p-> some_flag' के मान को कैश नहीं कर सकता है? मुझे स्मृति बाधा के प्रभावों के बारे में निश्चित नहीं है। उन्हें थोड़ा सा समझाते हुए मन? –

+0

उत्तर संपादित किया गया। क्या अब यह स्पष्ट है? –

+0

हां, धन्यवाद। –

3

लिखा के रूप में, संकलक परिणाम कैश करने के लिए के रूप में आप का वर्णन या यहाँ तक कि एक और अधिक सूक्ष्म तरीके से मुक्त है। आप परिवर्तनीय volatile बनाकर इस अनुकूलन को होने से रोक सकते हैं। लेकिन यह जरूरी नहीं है - आपको इसे इस तरह से कोड नहीं करना चाहिए! आपको शर्त वैरिएबल का उपयोग निर्धारित (लॉक, प्रतीक्षा, अनलॉक) के रूप में करना चाहिए।

लाइब्रेरी के आसपास काम करने की कोशिश करना बुरा है, लेकिन यह और भी खराब हो जाता है। शायद PLDI 2005 ("थ्रेड को लाइब्रेरी के रूप में कार्यान्वित नहीं किया जा सकता") से सामान्य विषय पर हंस बोहेम के पेपर को पढ़ना, या उसके कई follow-on articles (जो संशोधित सी ++ मेमोरी मॉडल पर काम करने के लिए नेतृत्व करते हैं) आपके अंदर भगवान का डर डाल देंगे और आपको सीधे और संकीर्ण पर वापस चलाएं :)।

+0

मैं पैसे का भुगतान किए बिना उस पेपर को नहीं पढ़ सकता।मैं सीखने के लिए बहुत गरीब हूं =/ –

+1

तकनीकी रिपोर्ट के लिए वैकल्पिक मुफ्त लिंक: http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html – EmeryBerger

+0

लिंक के लिए धन्यवाद। मुझे हंस के कागजात पढ़ने से प्यार है। हालांकि वे बहुत मजेदार हैं। मैं अपने जीवन का एक घंटा खोने जा रहा हूँ। –

7

सामान्य स्थिति में, यहां तक ​​कि अगर बहु ​​सूत्रण शामिल नहीं किया गया है और अपने पाश की तरह देखा:

void check_flag(foo_t* f) { 
    while(f->flag) 
     foo(&f->c, &f->m); 
} 

संकलक f->flag परीक्षण कैश करने के लिए करने में असमर्थ होगा। ऐसा इसलिए है क्योंकि संकलक यह नहीं जानता कि कोई फ़ंक्शन (जैसे foo() ऊपर) चाहे जो भी ऑब्जेक्ट f इंगित कर रहा हो।

विशेष परिस्थितियों में (foo() संकलक को दिखाई देता है, और सभी संकेत check_flag() को पास नहीं एलियास या अन्यथा foo() द्वारा परिवर्तनीय माने जाते हैं) संकलक जांच अनुकूलन करने के लिए सक्षम हो सकता है।

हालांकि, pthread_cond_wait() को उस तरीके से कार्यान्वित किया जाना चाहिए जो उस अनुकूलन को रोक देगा।

Does guarding a variable with a pthread mutex guarantee it's also not cached? देखें: Can a C/C++ compiler legally cache a variable in a register across a pthread library call?

लेकिन कितनी दूर आप अपने खुद के काम में मुद्दों बोहम पेपर द्वारा उठाए ले जाना चाहते हैं आप पर निर्भर है:

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

+0

मेरी इच्छा है कि मैं दो उत्तरों स्वीकार कर सकता हूं, लेकिन मैं नहीं कर सकता। मैंने अभी सबसे कम प्रतिनिधि के साथ एक चुना है। हालांकि वे समान रूप से सहायक थे! –

+1

यह सबसे अच्छा जवाब है, लेकिन मुझे दूसरे को स्वीकार करने के लिए ओपी के तर्क पसंद हैं। :-) इसके लायक होने के लिए, pthread सिंक्रनाइज़ेशन फ़ंक्शंस पूर्ण मेमोरी बाधाओं के लिए निर्दिष्ट हैं। यह कैसे लागू किया जाता है एप्लिकेशन का कोई भी व्यवसाय नहीं है; यह सिर्फ काम करने की गारंटी है। –

+0

"_pthread_cond_wait() को इस तरह से कार्यान्वित किया जाना चाहिए जो उस अनुकूलन को रोक देगा ._" मुझे उत्सुकता है कि कैसे 'pthread_cond_wait' को उचित तरीके से कार्यान्वित किया जा सकता है जो (गलत) अनुकूलन की अनुमति देता है! – curiousguy

1

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

मजेदार छोटी नोट की तरह। हमारे पास एक वॉलेटाइल # परिभाषित है जो या तो "अस्थिर" है (जब हमें लगता है कि बग संभवतः हमारा कोड नहीं हो सकता ...) या खाली। जब हमें लगता है कि हमारे पास ऑप्टिमाइज़र की हत्या के कारण हमें दुर्घटना हुई है, तो हम इसे "अस्थिर" परिभाषित करते हैं जो लगभग हर चीज के सामने अस्थिर रखता है। फिर हम यह देखने के लिए परीक्षण करते हैं कि समस्या दूर हो गई है या नहीं। अब तक ... बग डेवलपर रहे हैं और संकलक नहीं! किसने सोचा होगा !? हमने उच्च प्रदर्शन "गैर लॉकिंग" और "गैर अवरोधन" थ्रेडिंग लाइब्रेरी विकसित की है। हमारे पास एक परीक्षण मंच है जो प्रति सेकंड हजारों दौड़ के बिंदु पर इसे हथियार देता है। तो किराया, हमने अस्थिरता की आवश्यकता में कोई समस्या नहीं देखी है! अब तक जीसीसी ने एक रजिस्टर में एक साझा चर को कैश नहीं किया है। हाँ ... हम भी आश्चर्यचकित हैं। हम अभी भी अस्थिरता का उपयोग करने के हमारे मौके की प्रतीक्षा कर रहे हैं!

+0

मुझे कहानी पसंद है =) एक अपवर्त है। –

+0

"_compilers इन दिनों बहुत स्मार्ट हैं ._" कंपाइलर ने कब माना कि किसी फ़ंक्शन पर कॉल का कोई प्रभाव नहीं पड़ा? – curiousguy