बिना शर्त असाइनमेंट के रूप में C ++ कंपाइलर इस सशर्त बूलियन असाइनमेंट का अनुकूलन क्यों नहीं करते हैं?


117

निम्नलिखित कार्य पर विचार करें:

void func(bool& flag)
{
    if(!flag) flag=true;
}

यह मुझे लगता है कि यदि ध्वज का एक वैध बूलियन मूल्य है, तो यह इस trueतरह से बिना शर्त सेटिंग के बराबर होगा :

void func(bool& flag)
{
    flag=true;
}

फिर भी न तो जीसीसी और न ही क्लैग इसे इस तरह से अनुकूलित करते हैं - दोनों -O3अनुकूलन स्तर पर निम्नलिखित उत्पन्न करते हैं :

_Z4funcRb:
.LFB0:
    .cfi_startproc
    cmp BYTE PTR [rdi], 0
    jne .L1
    mov BYTE PTR [rdi], 1
.L1:
    rep ret

मेरा प्रश्न यह है: क्या यह सिर्फ इतना है कि कोड का अनुकूलन करने के लिए देखभाल करने के लिए बहुत विशेष-मामला है, या क्या कोई अच्छा कारण है कि इस तरह के अनुकूलन को अवांछित क्यों किया जाएगा, यह देखते हुए कि flagकोई संदर्भ नहीं है volatile? यह एकमात्र कारण लगता है जो हो सकता है कि flagयह किसी भी तरह का गैर- trueया- falseमान हो सकता है , इसे पढ़ने के बिंदु पर अपरिभाषित व्यवहार के बिना, लेकिन मुझे यकीन नहीं है कि यह संभव है।


8
क्या आपके पास कोई सबूत है कि यह "अनुकूलन" है?
डेविड श्वार्ट्ज

1
@ 200_ असफल मुझे नहीं लगता कि शीर्षक के रूप में गैर-कार्यशील मार्कअप के साथ कोड की एक पंक्ति डालना एक अच्छी बात है। यदि आप एक अधिक विशिष्ट शीर्षक चाहते हैं, तो ठीक है, लेकिन एक अंग्रेजी वाक्य चुनें , और इसमें कोड से बचने की कोशिश करें (जैसे कि संकलक सशर्त का अनुकूलन नहीं करता है बिना शर्त लिखता है जब वे उन्हें समकक्ष साबित कर सकते हैं? या समान)। इसके अलावा, चूंकि बैकटिक्स का प्रतिपादन नहीं किया गया है, इसलिए यदि आप कोड का उपयोग करते हैं तो भी शीर्षक में उनका उपयोग न करें।
बाकुरू

2
@Ruslan, जबकि फ़ंक्शन के लिए यह ऑप्टिमाइज़ेशन स्वयं नहीं लगता है, जब यह कोड को इनलाइन कर सकता है, तो यह इनलाइन संस्करण के लिए ऐसा करना प्रतीत होता है। अक्सर बस एक संकलन समय में जिसके परिणामस्वरूप 1इस्तेमाल किया जा रहा है। Godbolt.org/g/swe0tc
इवान टेरान

जवाबों:


102

यह कैश सुसंगतता के कारण कार्यक्रम के प्रदर्शन को नकारात्मक रूप से प्रभावित कर सकता है । flagहर बार लिखने को func()कहा जाता है कि युक्त कैश लाइन को गंदा कर देगा। यह इस तथ्य की परवाह किए बिना होगा कि लिखा जा रहा मूल्य लिखने से पहले गंतव्य पते पर पाए गए बिट्स से बिल्कुल मेल खाता है।


संपादित करें

hvd ने एक और अच्छा कारण प्रदान किया है जो इस तरह के अनुकूलन को रोकता है। यह प्रस्तावित अनुकूलन के खिलाफ एक अधिक सम्मोहक तर्क है, क्योंकि इसके परिणामस्वरूप अपरिभाषित व्यवहार हो सकता है, जबकि मेरा (मूल) उत्तर केवल संबोधित किए गए पहलुओं को ही बताता है।

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

const bool foo = true;

int main()
{
    func(const_cast<bool&>(foo));
}

बिना शर्त लिखने के साथ func() निश्चित रूप से अपरिभाषित व्यवहार शुरू होता है (केवल पढ़ने के लिए लिखना कार्यक्रम को समाप्त कर देगा, भले ही लेखन का प्रभाव अन्यथा नहीं होगा)।


7
शाखा से छुटकारा पाने के बाद से यह प्रदर्शन पर भी सकारात्मक प्रभाव डाल सकता है। इसलिए मुझे नहीं लगता कि यह विशेष मामला बहुत विशिष्ट प्रणाली को ध्यान में रखे बिना चर्चा करने के लिए सार्थक है।
लुंडिन

3
@Yakk व्यवहार परिभाषितता लक्ष्य मंच से प्रभावित नहीं है। यह कहते हुए कि यह कार्यक्रम को समाप्त कर देगा गलत है, लेकिन यूबी के स्वयं के दूरगामी परिणाम हो सकते हैं, जिसमें नाक के राक्षस भी शामिल हैं।
जॉन ड्वोरक

16
@ यकायक यह निर्भर करता है कि "रीड-ओनली मेमोरी" से क्या मतलब है। नहीं, यह एक ROM चिप में नहीं है, लेकिन यह अक्सर एक पृष्ठ पर लोड किए गए अनुभाग में होता है, जिसमें लिखने की पहुंच सक्षम नहीं होती है, और आपको एक SIGSEGV संकेत या STATUS_ACCESS_VIOLATION अपवाद मिलेगा जब इसे लिखने का प्रयास करें।
रैंडम 832

5
"यह निश्चित रूप से अपरिभाषित व्यवहार को ट्रिगर करता है"। सं। अपरिभाषित व्यवहार अमूर्त मशीन की एक संपत्ति है। यह वही है जो कोड कहता है कि यह निर्धारित करता है कि क्या यूबी मौजूद है। कंपाइलर इसका कारण नहीं बन सकते (हालांकि यदि छोटी गाड़ी कंपाइलर प्रोग्राम को गलत तरीके से व्यवहार करने का कारण बन सकता है)।
एरिक एम श्मिट

7
यह constएक फ़ंक्शन में पास करने के लिए कास्टिंग-दूर है जो डेटा को संशोधित कर सकता है जो अपरिभाषित व्यवहार का स्रोत है, न कि बिना शर्त के। डॉक्टर, यह दर्द होता है जब मैं ऐसा करता हूं ....
स्पेन्सर

48

प्रदर्शन पर लियोन के जवाब के अलावा:

मान लीजिए flagहै true। मान लीजिए दो धागे लगातार फोन कर रहे हैं func(flag)। जैसा कि लिखा गया है, उस मामले में फ़ंक्शन कुछ भी संग्रहीत नहीं करता है flag, इसलिए यह थ्रेड-सुरक्षित होना चाहिए। दो धागे एक ही मेमोरी का उपयोग करते हैं, लेकिन केवल इसे पढ़ने के लिए। बिना शर्त दो अलग-अलग थ्रेड्स flagका trueमतलब एक ही मेमोरी में लिखना होगा। यह सुरक्षित नहीं है, यह असुरक्षित है भले ही लिखा जा रहा डेटा पहले से मौजूद डेटा के समान हो।


9
मुझे लगता है कि यह आवेदन करने का एक परिणाम है [intro.races]/21
ग्रीव्स

10
बहुत ही रोचक। इसलिए मैंने इसे इस रूप में पढ़ा: संकलक को कभी भी एक लिखने के ऑपरेशन में "ऑप्टिमाइज़" करने की अनुमति नहीं होती है जहां अमूर्त मशीन में एक नहीं होगा।
मार्टिन बा

3
@MartinBa ज्यादातर ऐसा है। लेकिन अगर संकलक यह साबित कर सकता है कि यह कोई फर्क नहीं पड़ता, उदाहरण के लिए क्योंकि यह साबित कर सकता है कि कोई अन्य धागा संभवतः उस विशेष चर तक पहुंच नहीं सकता है, तो यह ठीक हो सकता है।

13
यह केवल असुरक्षित है यदि सिस्टम संकलक को लक्षित कर रहा है तो यह असुरक्षित बना देता है । मैं कभी भी एक ऐसी प्रणाली पर विकसित नहीं हुआ जहां 0x01एक बाइट को लिखना जो पहले से ही 0x01"असुरक्षित" व्यवहार का कारण है। शब्द या डॉर्ड मेमोरी एक्सेस वाली प्रणाली पर यह होगा; लेकिन आशावादी को इसके बारे में पता होना चाहिए। एक आधुनिक पीसी या फोन ओएस पर, कोई समस्या नहीं होती है। तो यह एक वैध कारण नहीं है।
यक्क - एडम नेवेरुमोंट

4
@ यक वास्तव में, और भी अधिक सोचता हूं, मुझे लगता है कि यह बिल्कुल सही है, यहां तक ​​कि आम प्रोसेसर के लिए भी। मुझे लगता है कि आप सही हैं जब सीपीयू सीधे मेमोरी को लिख सकता है, लेकिन मान लें कि flagएक कॉपी-ऑन-राइट पेज है। अब, सीपीयू स्तर पर, व्यवहार को परिभाषित किया जा सकता है (पृष्ठ दोष, ओएस को इसे संभालने दें), लेकिन ओएस स्तर पर, यह अभी भी अपरिभाषित हो सकता है, है ना?

13

मुझे यहाँ C ++ के व्यवहार के बारे में निश्चित नहीं है, लेकिन C में मेमोरी बदल सकती है क्योंकि यदि मेमोरी में 1 के अलावा एक गैर-शून्य मान है, तो यह चेक के साथ अपरिवर्तित रहेगा, लेकिन चेक के साथ 1 में बदल गया।

लेकिन जैसा कि मैं सी ++ में बहुत धाराप्रवाह नहीं हूं, मुझे नहीं पता कि क्या यह स्थिति संभव है।


क्या यह अभी भी सच होगा _Bool?
रुस्लान

5
C में, यदि मेमोरी में ऐसा मान है जो ABI नहीं कहता है कि वह अपने प्रकार के लिए मान्य है, तो यह एक ट्रैप प्रतिनिधित्व है, और एक ट्रैप प्रतिनिधित्व पढ़ना अपरिभाषित व्यवहार है। C ++ में, यह केवल तब हो सकता है जब एक uninitialised ऑब्जेक्ट को पढ़ रहा हो, और यह एक uninitialised ऑब्जेक्ट को पढ़ रहा हो जो UB हो। लेकिन अगर आप एक ऐसा ABI पा सकते हैं जो कहता है कि कोई भी गैर-शून्य मान प्रकार bool/ _Boolऔर साधनों के लिए मान्य है true, तो उस विशेष ABI में, आप सही हैं।

1
@Ruslan संकलक के साथ जो इटेनियम एबीआई का उपयोग करते हैं, और एआरएम प्रोसेसर पर, सी _Boolऔर सी ++ boolया तो एक ही प्रकार, या संगत प्रकार हैं जो समान नियमों का पालन करते हैं। MSVC के साथ, उनके पास समान आकार और संरेखण है, लेकिन इस बारे में कोई आधिकारिक बयान नहीं है कि वे समान नियमों का उपयोग करते हैं या नहीं।
जस्टिन टाइम -

1
@JustinTime: C <stdbool.h>में एक typedef _Bool bool; और हाँ शामिल है , x86 पर (कम से कम सिस्टम V ABI में), bool/ _Boolया तो 0 या 1 होना आवश्यक है, बाइट के ऊपरी बिट्स के साथ। मुझे नहीं लगता कि यह स्पष्टीकरण प्रशंसनीय है।
पीटर कॉर्डेस

1
@JustinTime: यह सच है, मुझे सिर्फ यह बताना चाहिए था कि निश्चित रूप से सिस्टम V ABI के सभी x86 फ्लेवर में एक ही शब्दार्थ है, यही सवाल था। (मैं बता सकता हूं क्योंकि पहला आरजीआई funcआरडीआई में पारित किया गया था, जबकि विंडोज आरडीएक्स का उपयोग करेगा)।
पीटर कॉर्डेस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.