2011-11-14 17 views
10

मैं अपने स्वयं के atomic टेम्पलेट का उपयोग करके, कुछ समय के लिए जीसीसी के इंटेल-संगत बिल्टिन (जैसे __sync_fetch_and_add) का उपयोग कर रहा हूं। "__sync" कार्यों को आधिकारिक तौर पर "विरासत" माना जाता है।जीसीसी std :: परमाणु वृद्धि क्यों अक्षम गैर-परमाणु असेंबली उत्पन्न कर रही है?

सी ++ 11 std::atomic<> और इसके वंशजों का समर्थन करता है, इसलिए इसके बजाय इसका उपयोग करना उचित लगता है, क्योंकि यह मेरे कोड मानक अनुपालन करता है, और संकलक एक प्लेटफार्म स्वतंत्र तरीके से किसी भी तरह से सर्वोत्तम कोड का उत्पादन करेगा, जो कि है सच होने के लिए लगभग बहुत अच्छा है।
संयोग से, मुझे केवल atomic को std::atomic के साथ टेक्स्ट-प्रतिस्थापित करना होगा। std::atomic (पुनः: मेमोरी मॉडल) में बहुत कुछ है जिसे मुझे वास्तव में आवश्यकता नहीं है, लेकिन डिफ़ॉल्ट पैरामीटर इसका ख्याल रखते हैं।

अब बुरी खबरों के लिए। जैसा कि यह पता चला है, जेनरेट कोड है, जो मैं बता सकता हूं, ... बकवास बकवास, और यहां तक ​​कि परमाणु भी नहीं। यहां तक ​​कि एक न्यूनतम उदाहरण जो कि एक परमाणु चर को बढ़ाता है और आउटपुट करता है, इसमें 5 से कम गैर-इनलाइन फ़ंक्शन कॉल ___atomic_flag_for_address, ___atomic_flag_wait_explicit, और __atomic_flag_clear_explicit (पूरी तरह अनुकूलित) हैं, और दूसरी तरफ, एक एकल परमाणु निर्देश नहीं है उत्पन्न निष्पादन योग्य।

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

इतने सारे फ़ंक्शन कॉल के पीछे "तर्क" क्या है, और परमाणुता परमाणुता के बिना कैसे लागू की जाती है?

के रूप में-सरल-के रूप में यह कर सकते हैं-मिल उदाहरण:

#include <atomic> 

int main() 
{ 
    std::atomic_int a(5); 
    ++a; 
    __builtin_printf("%d", (int)a); 
    return 0; 
} 

का उत्पादन निम्नलिखित .s:

movl $5, 28(%esp)  #, a._M_i 
movl %eax, (%esp)  # tmp64, 
call ___atomic_flag_for_address # 
movl $5, 4(%esp) #, 
movl %eax, %ebx #, __g 
movl %eax, (%esp)  # __g, 
call ___atomic_flag_wait_explicit  # 
movl %ebx, (%esp)  # __g, 
addl $1, 28(%esp)  #, MEM[(__i_type *)&a] 
movl $5, 4(%esp) #, 
call _atomic_flag_clear_explicit # 
movl %ebx, (%esp)  # __g, 
movl $5, 4(%esp) #, 
call ___atomic_flag_wait_explicit  # 
movl 28(%esp), %esi # MEM[(const __i_type *)&a], __r 
movl %ebx, (%esp)  # __g, 
movl $5, 4(%esp) #, 
call _atomic_flag_clear_explicit # 
movl $LC0, (%esp)  #, 
movl %esi, 4(%esp) # __r, 
call _printf # 
(...) 
.def ___atomic_flag_for_address; .scl 2; .type 32; .endef 
.def ___atomic_flag_wait_explicit; .scl 2; .type 32; .endef 
.def _atomic_flag_clear_explicit; .scl 2; .type 32; .endef 

... और उल्लेख किया कार्यों जैसे देखो objdump में इस तरह:

004013c4 <__atomic_flag_for_address>: 
mov 0x4(%esp),%edx 
mov %edx,%ecx 
shr $0x2,%ecx 
mov %edx,%eax 
shl $0x4,%eax 
add %ecx,%eax 
add %edx,%eax 
mov %eax,%ecx 
shr $0x7,%ecx 
mov %eax,%edx 
shl $0x5,%edx 
add %ecx,%edx 
add %edx,%eax 
mov %eax,%edx 
shr $0x11,%edx 
add %edx,%eax 
and $0xf,%eax 
add $0x405020,%eax 
ret  

दूसरों को कुछ हद तक सरल हैं, लेकिन मैं एक एकल अनुदेश कि वास्तव में परमाणु होगा (कुछ नकली xchg के अलावा अन्य जो X86 पर परमाणु कर रहे हैं नहीं है, लेकिन इन करने लगते हैं बजाय एनओपी/पैडिंग हो, क्योंकि xchg %ax,%axret के बाद)।

मुझे बिल्कुल यकीन नहीं है कि इस तरह के जटिल कार्य की आवश्यकता क्यों है, और यह किसी भी परमाणु बनाने के लिए कैसे है।

+0

जीसीसी का आप किस संस्करण का उपयोग कर रहे हैं? क्या आप एक छोटा प्रोग्राम दिखा सकते हैं जिसके परिणामस्वरूप खराब कोड हो? मैं पिछले महीने से 4.7 स्नैपशॉट चला रहा हूं और ऐसा लगता है कि इसमें 'लॉक' निर्देशों के साथ सभ्य कोड उत्पन्न होता है। –

+1

मेमोरी मॉडल जिसे आपको "आवश्यकता नहीं है" एक संभावित अपराधी के रूप में दिमाग में आता है। आपका कोड कैसा दिखता है? आखिरी वाक्य के साथ आपका क्या मतलब है: "परमाणुता के बिना परमाणुता कैसे लागू की जाती है"? – jalf

+0

"मेमोरी मॉडल" द्वारा, क्या आपका मतलब "मेमोरी ऑर्डरिंग" है? –

उत्तर

14

यह एक अपर्याप्त कंपाइलर निर्माण है।

अपने c++config.h, यह इस तरह दिखना shoukld की जाँच करें, लेकिन यह नहीं है:

/* Define if builtin atomic operations for bool are supported on this host. */ 
#define _GLIBCXX_ATOMIC_BUILTINS_1 1 

/* Define if builtin atomic operations for short are supported on this host. 
    */ 
#define _GLIBCXX_ATOMIC_BUILTINS_2 1 

/* Define if builtin atomic operations for int are supported on this host. */ 
#define _GLIBCXX_ATOMIC_BUILTINS_4 1 

/* Define if builtin atomic operations for long long are supported on this 
    host. */ 
#define _GLIBCXX_ATOMIC_BUILTINS_8 1 

ये मैक्रो निर्धारित नहीं है या configure परीक्षण है, जो जाँच __sync_XXX कार्यों के लिए मेजबान मशीन समर्थन के आधार पर नहीं। ये परीक्षण libstdc++v3/acinclude.m4, AC_DEFUN([GLIBCXX_ENABLE_ATOMIC_BUILTINS] ... में हैं।

अपनी स्थापना पर, यह -fverbose-asm द्वारा विधानसभा फ़ाइल में MEM[(__i_type *)&a] पुट कि संकलक atomic_0.h से मैक्रो का उपयोग करता है से स्पष्ट है उदाहरण के लिए,:,

#define _ATOMIC_LOAD_(__a, __x)      \ 
    ({typedef __typeof__(_ATOMIC_MEMBER_) __i_type;       \ 
    __i_type* __p = &_ATOMIC_MEMBER_;      \ 
    __atomic_flag_base* __g = __atomic_flag_for_address(__p);   \ 
    __atomic_flag_wait_explicit(__g, __x);     \ 
    __i_type __r = *__p;       \ 
    atomic_flag_clear_explicit(__g, __x);      \ 
    __r; }) 
एक ठीक से बनाया संकलक के साथ

अपने उदाहरण कार्यक्रम के साथ, c++ -m32 -std=c++0x -S -O2 -march=core2 -fverbose-asm इस तरह कुछ उत्पादन करना चाहिए:

movl $5, 28(%esp) #, a.D.5442._M_i 
lock addl $1, 28(%esp) #, 
mfence 
movl 28(%esp), %eax # MEM[(const struct __atomic_base *)&a].D.5442._M_i, __ret 
mfence 
movl $.LC0, (%esp) #, 
movl %eax, 4(%esp) # __ret, 
call printf # 
+1

और अनुमान लगाएं कि, परिभाषित करने के लिए 'C++ config.h' को संपादित करने से समस्या ठीक हो जाती है, जिससे मुझे ऊपर 'पोस्ट लॉक एडीएल, माफेंस' अनुक्रम दिया जाता है, जो मैं चाहता हूं, जो भी मैं चाहता हूं। (मैं इस मुद्दे को अपने कंपाइलर निर्माता को अग्रेषित करूंगा)। आपका बहुत बहुत धन्यवाद। – Damon

3

दो कार्यान्वयन हैं। एक जो __sync प्राइमेटिव्स का उपयोग करता है और जो नहीं करता है। इसके अलावा दोनों का मिश्रण जो केवल उन प्राइमेटिव्स का उपयोग करता है। जो चुना गया है मैक्रोज़ _GLIBCXX_ATOMIC_BUILTINS_1, _GLIBCXX_ATOMIC_BUILTINS_2, _GLIBCXX_ATOMIC_BUILTINS_4 और _GLIBCXX_ATOMIC_BUILTINS_8 पर निर्भर करता है।

कम से कम पहले मिश्रित कार्यान्वयन के लिए आवश्यक है, सभी को पूरी तरह से परमाणु के लिए जरूरी है। यह seems है कि वे परिभाषित हैं कि लक्ष्य मशीन पर निर्भर करता है (उन्हें -mi386 के लिए परिभाषित नहीं किया जा सकता है और -mi686 के लिए परिभाषित किया जाना चाहिए)।

+0

इनमें से कोई भी यहां परिभाषित नहीं किया गया है हालांकि परमाणु insns निश्चित रूप से उपलब्ध हैं (मैं '-march = core2' के लिए संकलित कर रहा हूं) और' __sync' फ़ंक्शंस का उपयोग कर समस्या के बिना काम करता हूं। मैंने '' को शामिल करने से पहले इन मैक्रोज़ को परिभाषित करने का प्रयास किया है, यह देखने के लिए कि क्या इससे कोई फर्क पड़ता है, हालांकि नहीं। तो मूल रूप से आप कह रहे हैं कि यह शायद "गरीब आदमी के फॉलबैक कार्यान्वयन" का एक प्रकार है? उस स्थिति में, मैं असली कैसे सक्षम करूं (अपने स्वयं के जीसीसी को संकलित किए बिना)? – Damon

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