मैं अपने स्वयं के 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,%ax
ret
के बाद)।
मुझे बिल्कुल यकीन नहीं है कि इस तरह के जटिल कार्य की आवश्यकता क्यों है, और यह किसी भी परमाणु बनाने के लिए कैसे है।
जीसीसी का आप किस संस्करण का उपयोग कर रहे हैं? क्या आप एक छोटा प्रोग्राम दिखा सकते हैं जिसके परिणामस्वरूप खराब कोड हो? मैं पिछले महीने से 4.7 स्नैपशॉट चला रहा हूं और ऐसा लगता है कि इसमें 'लॉक' निर्देशों के साथ सभ्य कोड उत्पन्न होता है। –
मेमोरी मॉडल जिसे आपको "आवश्यकता नहीं है" एक संभावित अपराधी के रूप में दिमाग में आता है। आपका कोड कैसा दिखता है? आखिरी वाक्य के साथ आपका क्या मतलब है: "परमाणुता के बिना परमाणुता कैसे लागू की जाती है"? – jalf
"मेमोरी मॉडल" द्वारा, क्या आपका मतलब "मेमोरी ऑर्डरिंग" है? –