अस्थिरता क्यों होती है?


222

volatileकीवर्ड क्या करता है? C ++ में यह किस समस्या का समाधान करता है?

मेरे मामले में, मुझे कभी जानबूझकर इसकी जरूरत नहीं पड़ी।



3
एक पेचीदा तकनीक है जो आपके संकलक को संभावित दौड़ की स्थिति का पता लगाती है जो कि अस्थिर कीवर्ड पर बहुत अधिक निर्भर करती है, आप इसके बारे में http://www.ddj.com/cpp/184403766 पर पढ़ सकते हैं ।
नीनो गनेशेव

यह एक अच्छा संसाधन है जिस पर एक उदाहरण दिया गया है जब volatileप्रभावी रूप से इस्तेमाल किया जा सकता है, एक साथ सुंदर आम शब्दों में प्रस्तुत किया जा सकता है। लिंक: publications.gbdirect.co.uk/c_book/chapter8/…
अनुकूलित कोडर

मैं इसका उपयोग लॉक फ्री कोड / डबल चेक किए गए लॉकिंग के लिए करता
हूं

मेरे लिए, कीवर्ड volatileसे अधिक उपयोगी है friend
इक्का

जवाबों:


268

volatile जरूरत है अगर आप स्मृति में एक जगह से पढ़ रहे हैं, जो कहते हैं, एक पूरी तरह से अलग प्रक्रिया / उपकरण / जो कुछ भी लिख सकता है।

मैं सीधे सी में एक मल्टीप्रोसेसर सिस्टम में दोहरे-पोर्ट रैम के साथ काम करता था। हमने एक हार्डवेयर का इस्तेमाल किया जब वह दूसरे आदमी के होने का पता करने के लिए एक सेमाफोर के रूप में 16 बिट मूल्य का प्रबंधन करता था। अनिवार्य रूप से हमने ऐसा किया:

void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

इसके बिना volatile, आशावादी पाश को बेकार के रूप में देखता है (आदमी कभी भी मूल्य निर्धारित नहीं करता है! वह पागल है, उस कोड से छुटकारा पाएं!) और मेरा कोड सेमीफोर हासिल किए बिना आगे बढ़ेगा, जिससे बाद में समस्याएं हो सकती हैं।


इस मामले में, अगर uint16_t* volatile semPtrइसके बजाय लिखा होता तो क्या होता ? इससे पॉइंटर को अस्थिर के रूप में चिह्नित किया जाना चाहिए (इंगित किए गए मूल्य के बजाय), ताकि पॉइंटर की जांच स्वयं हो, उदाहरण के semPtr == SOME_ADDRलिए अनुकूलित नहीं किया जा सकता है। यह हालांकि एक अस्थिर संकेत मूल्य के रूप में अच्छी तरह से फिर से मतलब है। नहीं?
ज़िल

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

1
@ डग टी। एक बेहतर व्याख्या यह है
machineaddict

3
@ क्रिस्च्युगी ने गलत तरीके से फैसला नहीं किया। इसने दी गई जानकारी के आधार पर सही कटौती की। यदि आप कुछ अस्थिर चिह्नित करने में विफल रहते हैं, तो संकलक यह मानने के लिए स्वतंत्र है कि यह अस्थिर नहीं है । कोड का अनुकूलन करते समय संकलक यही करता है। यदि अधिक जानकारी है, अर्थात्, कहा गया कि डेटा वास्तव में अस्थिर है, तो यह प्रोग्रामर की जिम्मेदारी है कि वह जानकारी प्रदान करे। क्या आप एक छोटी गाड़ी संकलक द्वारा दावा कर रहे हैं वास्तव में सिर्फ बुरा प्रोग्रामिंग है।
इहानी

1
@curiousguy नहीं, सिर्फ इसलिए कि अस्थिर कीवर्ड प्रकट होता है एक बार मतलब नहीं है कि सब कुछ अचानक अस्थिर हो जाता है। मैंने एक परिदृश्य दिया जहां कंपाइलर सही काम करता है और एक परिणाम प्राप्त करता है जो कि प्रोग्रामर गलत तरीके से अपेक्षा करता है। जैसे "मोस्ट वेस्टिंग पार्स" कंपाइलर एरर का चिन्ह नहीं है, न ही यहाँ ऐसा है।
इहानी जू १18

82

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


9
यह न केवल एम्बेडेड सिस्टम के लिए बल्कि सभी डिवाइस ड्राइवरों के विकास के लिए मान्य है।
म्लादेन जेन्कोविएव

केवल एक बार जब मुझे इसकी आवश्यकता 8bit ISA बस पर पड़ी, जहां आपने एक ही पते पर दो बार पढ़ा था - कंपाइलर में एक बग था और इसे अनदेखा किया (शुरुआती ज़ोर्टेक सी ++)
मार्टिन बेकेट

बाहरी उपकरणों के नियंत्रण के लिए अस्थिर बहुत कम है। आधुनिक MMIO के लिए इसका शब्दार्थ गलत है: आपको बहुत सी वस्तुओं को अस्थिर करना होगा और यह अनुकूलन को नुकसान पहुँचाती है। लेकिन आधुनिक एमएमआईओ सामान्य स्मृति की तरह व्यवहार करता है जब तक कि एक ध्वज सेट नहीं किया जाता है, तब तक अस्थिर की आवश्यकता नहीं होनी चाहिए। कई ड्राइवर कभी भी अस्थिर का उपयोग नहीं करते हैं।
जिज्ञासु

69

कुछ प्रोसेसरों में फ्लोटिंग पॉइंट रजिस्टर होते हैं जिनमें 64 बिट्स से अधिक सटीकता होती है (जैसे कि एसएसई के बिना 32-बिट x86, पीटर की टिप्पणी देखें)। इस प्रकार, यदि आप दोहरे-सटीक संख्याओं पर कई ऑपरेशन चलाते हैं, तो आपको वास्तव में उच्च-सटीक उत्तर मिलता है, यदि आप प्रत्येक मध्यवर्ती परिणाम को 64 बिट्स तक कम करना चाहते थे।

यह आम तौर पर बहुत अच्छा है, लेकिन इसका मतलब है कि संकलक ने रजिस्टरों को कैसे सौंपा और क्या अनुकूलन किया, इसके आधार पर आपके पास सटीक एक ही इनपुट पर एक ही संचालन के लिए अलग-अलग परिणाम होंगे। यदि आपको निरंतरता की आवश्यकता है तो आप प्रत्येक ऑपरेशन को वाष्पशील कीवर्ड का उपयोग करके मेमोरी में वापस जाने के लिए बाध्य कर सकते हैं।

यह कुछ एल्गोरिदम के लिए भी उपयोगी है जो कोई बीजीय अर्थ नहीं बनाते हैं, लेकिन फ्लोटिंग पॉइंट एरर को कम करते हैं, जैसे कहन समन। बीजगणितीय रूप से यह एक झपकी है, इसलिए इसे अक्सर गलत तरीके से अनुकूलित किया जाएगा जब तक कि कुछ मध्यवर्ती चर अस्थिर नहीं होते हैं।


5
जब आप संख्यात्मक व्युत्पन्न की गणना करते हैं, तो यह उपयोगी होता है, यह सुनिश्चित करने के लिए कि x + h - x == h आप hh = x + h - x को अस्थिर के रूप में परिभाषित करते हैं ताकि एक उचित डेल्टा की गणना की जा सके।
अलेक्जेंड्रे सी।

5
+1, वास्तव में मेरे अनुभव में एक ऐसा मामला था जब फ़्लोटिंग-पॉइंट कंप्यूटेशन ने डिबग और रिलीज़ में अलग-अलग परिणाम उत्पन्न किए, इसलिए एक कॉन्फ़िगरेशन के लिए लिखे गए यूनिट परीक्षण दूसरे के लिए असफल हो रहे थे। हमने इसे volatile doubleकेवल एक के बजाय एक फ़्लोटिंग-पॉइंट चर घोषित करने के माध्यम से हल किया double, ताकि यह सुनिश्चित हो सके कि आगे की संगणनाओं को जारी रखने से पहले इसे FPU परिशुद्धता से 64-बिट (RAM) परिशुद्धता तक छोटा कर दिया जाए। फ़्लोटिंग-पॉइंट त्रुटि के एक और अतिशयोक्ति के कारण परिणाम काफी भिन्न थे।
सर्ज रोजैग

"आधुनिक" की आपकी परिभाषा थोड़ी दूर है। केवल 32-बिट x86 कोड जो SSE / SSE2 से बचता है, इससे प्रभावित होता है, और यह 10 साल पहले भी "आधुनिक" नहीं था। MIPS / ARM / POWER सभी में 64-बिट हार्डवेयर रजिस्टर हैं, और इसलिए SSE2 के साथ x86 है। C ++ कार्यान्वयन x86-64 हमेशा SSE2 का उपयोग करते हैं, और कंपाइलरों के पास g++ -mfpmath=sse32-बिट x86 के लिए भी इसका उपयोग करने के विकल्प होते हैं । आप x87 का उपयोग करते समय भी हर जगहgcc -ffloat-store राउंडिंग को मजबूर करने के लिए उपयोग कर सकते हैं , या आप x87 परिशुद्धता को 53-बिट मंटिसा में सेट कर सकते हैं: randomascii.wordpress.com/2012/03/21/…
पीटर कॉर्ड्स

लेकिन फिर भी अच्छा जवाब, अप्रचलित x87 कोड-जीन के लिए, आप volatileहर जगह लाभ खोए बिना कुछ विशिष्ट स्थानों में गोलाई को मजबूर करने के लिए उपयोग कर सकते हैं ।
पीटर कॉर्डेस

1
या क्या मैं असंगत के साथ गलत को भ्रमित करता हूं?
चिपस्टर डेस

49

एक से "वाष्पशील एक वादा के रूप में" दान साक्स द्वारा लेख:

(...) एक अस्थिर वस्तु वह है जिसका मूल्य अनायास बदल सकता है। यही है, जब आप किसी वस्तु को अस्थिर घोषित करते हैं, तो आप संकलक को बता रहे हैं कि वस्तु स्थिति बदल सकती है, भले ही कार्यक्रम में कोई भी कथन इसे बदलने के लिए प्रकट न हो। "

volatileकीवर्ड के बारे में उनके तीन लेखों के लिंक यहां दिए गए हैं :


23

लॉक-फ्री डेटा संरचनाओं को लागू करते समय आप अस्थिर का उपयोग करें। अन्यथा कंपाइलर चर तक पहुंच को अनुकूलित करने के लिए स्वतंत्र है, जो शब्दार्थ को बदल देगा।

इसे दूसरे तरीके से रखने के लिए, वाष्पशील कंपाइलर को बताता है कि इस वैरिएबल तक पहुँच एक भौतिक मेमोरी रीड / राइट ऑपरेशन के अनुरूप होनी चाहिए।

उदाहरण के लिए, यह Win32 API में InterlockedIncrement कैसे घोषित किया जाता है:

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);

InterlockedIncrement का उपयोग करने में सक्षम होने के लिए आपको चर को अस्थिर घोषित करने की आवश्यकता नहीं है।
जिज्ञासु

यह उत्तर अब अप्रचलित है कि C ++ 11 प्रदान करता है std::atomic<LONG>जिससे आप लॉकलेस कोड को अधिक सुरक्षित रूप से लिख सकते हैं, जिसमें शुद्ध भार / शुद्ध भंडार अनुकूलित या पुन: व्यवस्थित या अन्य जो कुछ भी हो, की समस्याओं के बिना सुरक्षित रूप से लिखा जा सकता है।
पीटर कॉर्ड्स

10

एक बड़ा अनुप्रयोग जो मैंने 1990 के दशक की शुरुआत में काम किया था, उसमें सेटजम्प और लॉन्गजम्प का उपयोग करते हुए सी-आधारित अपवाद था। वाष्पशील कीवर्ड चर पर आवश्यक था जिनके मूल्यों को कोड के ब्लॉक में संरक्षित करने की आवश्यकता थी जो "पकड़" खंड के रूप में कार्य करते थे, ऐसा न हो कि उन संस्करणों को रजिस्टरों में संग्रहीत किया जाए और लॉन्गजम्प द्वारा मिटा दिया जाए।


10

स्टैंडर्ड सी में, उपयोग करने के स्थानों में से volatileएक सिग्नल हैंडलर के साथ है। वास्तव में, मानक सी में, आप सभी सुरक्षित रूप से एक सिग्नल हैंडलर में कर सकते हैं एक volatile sig_atomic_tचर को संशोधित कर सकते हैं , या जल्दी से बाहर निकल सकते हैं। दरअसल, AFAIK, यह मानक C का एकमात्र स्थान है जिसका उपयोग volatileअपरिभाषित व्यवहार से बचने के लिए आवश्यक है।

आईएसओ / आईईसी 9899: 2011 §7.14.1.1 signalफ़ंक्शन

¶5 यदि सिग्नल abortया raiseफ़ंक्शन को कॉल करने के परिणामस्वरूप सिग्नल के अलावा अन्य होता है , तो सिग्नल हैंडलर स्थिर या थ्रेड स्टोरेज अवधि के साथ किसी भी ऑब्जेक्ट को संदर्भित करता है, जो एक मान असाइन करने के अलावा अन्य लॉक-फ्री परमाणु ऑब्जेक्ट नहीं है, तो व्यवहार अपरिभाषित है। किसी ऑब्जेक्ट के रूप में घोषित किया गया है volatile sig_atomic_t, या सिग्नल हैंडलर मानक लाइब्रेरी में किसी भी फ़ंक्शन को abortफ़ंक्शन, _Exitफ़ंक्शन, quick_exitफ़ंक्शन या फ़ंक्शन के अलावा अन्य signalफ़ंक्शन के साथ सिग्नल संख्या के बराबर पहला तर्क देता है, जो सिग्नल के आह्वान के कारण होता है। हैंडलर। इसके अलावा, अगर इस तरह के कॉल से signalफ़ंक्शन में SIG_ERR रिटर्न होता है, तो मान errnoअनिश्चित होता है। 252)

252) यदि कोई संकेत एक एसिंक्रोनस सिग्नल हैंडलर द्वारा उत्पन्न होता है, तो व्यवहार अपरिभाषित है।

इसका मतलब है कि मानक सी में, आप लिख सकते हैं:

static volatile sig_atomic_t sig_num = 0;

static void sig_handler(int signum)
{
    signal(signum, sig_handler);
    sig_num = signum;
}

और बहुत कुछ नहीं।

POSIX एक बहुत अधिक उदार है कि आप एक सिग्नल हैंडलर में क्या कर सकते हैं, लेकिन अभी भी सीमाएं हैं (और सीमाओं में से एक यह है कि मानक I / O पुस्तकालय - printf()एट अल - सुरक्षित रूप से उपयोग नहीं किया जा सकता)।


7

एक एम्बेडेड के लिए विकासशील, मेरे पास एक लूप है जो एक चर पर जांचता है जिसे एक बाधा हैंडलर में बदला जा सकता है। "अस्थिर" के बिना, लूप एक नोप बन जाता है - जहां तक ​​संकलक बता सकता है, चर कभी नहीं बदलता है, इसलिए यह चेक दूर का अनुकूलन करता है।

एक ही चीज एक वैरिएबल पर लागू होती है जिसे अधिक पारंपरिक वातावरण में एक अलग थ्रेड में बदला जा सकता है, लेकिन वहां हम अक्सर सिंक्रोनाइज़ेशन कॉल करते हैं, इसलिए कंपाइलर अनुकूलन के साथ इतना मुफ़्त नहीं है।


7

मैंने इसका उपयोग डिबग बिल्ड में किया है जब कंपाइलर एक चर को अनुकूलित करने पर जोर देता है जिसे मैं कोड के रूप में चरण के रूप में देखना चाहता हूं।


7

इसे इच्छानुसार उपयोग करने के अलावा, वाष्पशील (टेम्पलेट) मेटाप्रोग्रामिंग में उपयोग किया जाता है। इसका उपयोग आकस्मिक ओवरलोडिंग को रोकने के लिए किया जा सकता है, क्योंकि वाष्पशील विशेषता (जैसे कि कास्ट) ओवरलोड रिज़ॉल्यूशन में भाग लेती है।

template <typename T> 
class Foo {
  std::enable_if_t<sizeof(T)==4, void> f(T& t) 
  { std::cout << 1 << t; }
  void f(T volatile& t) 
  { std::cout << 2 << const_cast<T&>(t); }

  void bar() { T t; f(t); }
};

यह कानूनी है; दोनों अधिभार संभावित रूप से कॉल करने योग्य हैं और लगभग समान हैं। volatileओवरलोड में कास्ट कानूनी है क्योंकि हम जानते हैं कि बार किसी भी तरह से गैर-वाष्पशील नहीं होगा Tvolatileसंस्करण सख्ती से बदतर है, हालांकि, तो कभी नहीं अधिभार संकल्प में चुना जाता है, तो नॉन-वोलाटाइल fउपलब्ध है।

ध्यान दें कि कोड वास्तव में कभी भी निर्भर नहीं करता है volatile मेमोरी एक्सेस ।


क्या आप एक उदाहरण के साथ इस पर विस्तार से बता सकते हैं? यह वास्तव में मुझे बेहतर समझने में मदद करेगा। धन्यवाद!
बाथटब

" अस्थिर ओवरलोड में कलाकार" एक कलाकार एक स्पष्ट रूपांतरण है। यह एक SYNTAX निर्माण है। बहुत से लोग उस भ्रम (मानक लेखकों को भी) बनाते हैं।
जिज्ञासु

6
  1. आपको इसे स्पिनलॉक के साथ-साथ कुछ (सभी?) लॉक-फ्री डेटा संरचनाओं को लागू करने के लिए उपयोग करना होगा
  2. परमाणु संचालन / निर्देशों के साथ इसका उपयोग करें
  3. कंपाइलर बग को दूर करने के लिए एक बार मेरी मदद की (अनुकूलन के दौरान गलत तरीके से उत्पन्न कोड)

5
आप लाइब्रेरी, कंपाइलर इंट्रिनिक्स या इनलाइन असेंबली कोड का उपयोग करके बेहतर हैं। अस्थिर अविश्वसनीय है।
ज़ैन लिंक्स

1
1 और 2 दोनों ही परमाणु संचालन का उपयोग करते हैं, लेकिन अस्थिर परमाणु परमाणु प्रदान नहीं करते हैं और परमाणु के प्लेटफ़ॉर्म-विशिष्ट कार्यान्वयन अस्थिरता का उपयोग करने की आवश्यकता को सुपरसीड करेंगे, इसलिए 1 और 2 के लिए, मैं असहमत हूं, आपको इनके लिए अस्थिरता की आवश्यकता नहीं है।

परमाणु शब्दार्थ प्रदान करने वाले अस्थिरता के बारे में कौन कुछ कहता है? मैंने कहा कि आपको परमाणु संचालन के साथ अस्थिरता का उपयोग करने की आवश्यकता है और अगर आपको नहीं लगता कि यह win32 एपीआई के इंटरलॉक किए गए संचालन की घोषणाओं पर सच है (इस आदमी ने अपने उत्तर में यह भी समझाया)
म्लादेन जेन्कोविव

4

volatileकीवर्ड वस्तुओं है कि तरीके कि संकलक द्वारा निर्धारित नहीं किया जा सकता में बदल सकते हैं पर किसी भी अनुकूलन लागू करने से संकलक को रोकने के लिए करना है।

घोषित की गई वस्तुएं volatileअनुकूलन से छोड़ी जाती हैं क्योंकि उनके मूल्यों को किसी भी समय वर्तमान कोड के दायरे से बाहर कोड द्वारा बदला जा सकता है। सिस्टम हमेशा volatileमेमोरी लोकेशन से किसी ऑब्जेक्ट के करंट वैल्यू को पढ़ता है, बजाय इसके रजिस्टर में अस्थायी रजिस्टर में इसकी वैल्यू रखने के, भले ही पिछला इंस्ट्रक्शन उसी ऑब्जेक्ट से वैल्यू मांगे।

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

1) ग्लोबल वैरिएबल्स को दायरे के बाहर एक बाधा सेवा दिनचर्या द्वारा संशोधित किया गया है।

2) बहु-थ्रेडेड अनुप्रयोग के भीतर वैश्विक चर।

यदि हम वाष्पशील क्वालिफायर का उपयोग नहीं करते हैं, तो निम्न समस्याएं उत्पन्न हो सकती हैं

1) अनुकूलन के चालू होने पर कोड अपेक्षा के अनुरूप काम नहीं कर सकता है।

2) कोड अपेक्षित रूप से काम नहीं कर सकता है जब इंटरप्ट को सक्षम और उपयोग किया जाता है।

वाष्पशील: एक प्रोग्रामर का सबसे अच्छा दोस्त

https://en.wikipedia.org/wiki/Volatile_(computer_programming)


आपके द्वारा पोस्ट किया गया लिंक अत्यंत पुराना है और वर्तमान सर्वोत्तम प्रथाओं को प्रतिबिंबित नहीं करता है।
टिम सेगिन

2

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

उदाहरण के लिए मैंने एक एम्बेडेड प्लेटफॉर्म पर काम किया था कंपाइलर एक चर के मूल्य के बारे में कुछ गलत तरीके से विस्फोट कर रहे थे। यदि कोड अनुकूलित नहीं था तो प्रोग्राम ठीक चलेगा। अनुकूलन के साथ (जो वास्तव में आवश्यक था क्योंकि यह एक महत्वपूर्ण दिनचर्या थी) कोड सही ढंग से काम नहीं करेगा। एकमात्र समाधान (हालांकि बहुत सही नहीं) 'दोषपूर्ण' चर को अस्थिर घोषित करना था।


3
यह एक गलत धारणा है कि यह संकलक वाष्पशील की पहुंच का अनुकूलन नहीं करता है। मानक अनुकूलन के बारे में कुछ नहीं जानता है। संकलक को यह निर्धारित करने की आवश्यकता है कि मानक क्या निर्धारित करता है, लेकिन यह किसी भी अनुकूलन को करने के लिए स्वतंत्र है जो सामान्य व्यवहार में हस्तक्षेप नहीं करता है।
टर्मिनस

3
मेरे अनुभव से gcc बांह में सभी अनुकूलन "बग" के 99.9% प्रोग्रामर की ओर से त्रुटियां हैं। इस उत्तर पर लागू होने पर कोई विचार नहीं। सामान्य विषय पर सिर्फ एक शेख़ी
ओडिन्थेनरड

@Terminus " यह एक गलत धारणा है कि संकलक, वाष्पशील तक पहुंच का अनुकूलन नहीं करता है " स्रोत?
जिज्ञासु

2

आपका प्रोग्राम बिना volatileकीवर्ड के भी काम करने लगता है ? शायद यही कारण है:

जैसा कि पहले उल्लेख किया गया है कि volatileकीवर्ड जैसे मामलों के लिए मदद करता है

volatile int* p = ...;  // point to some memory
while( *p!=0 ) {}  // loop until the memory becomes zero

एक बार बाहरी या गैर-इनलाइन फ़ंक्शन कहा जा रहा है, लेकिन लगभग कोई प्रभाव नहीं लगता है। उदाहरण के लिए:

while( *p!=0 ) { g(); }

तब या उसके साथ volatileलगभग एक ही परिणाम उत्पन्न होता है।

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

लेकिन उस दिन से सावधान रहें, जब आपका फ़ंक्शन जी () या तो इनलाइन हो जाता है (या तो स्पष्ट बदलाव के कारण या कंपाइलर / लिंकर चतुराई के कारण) तब आपका कोड टूट सकता है यदि आप volatileकीवर्ड भूल गए हैं !

इसलिए मैं volatileआपके प्रोग्राम को बिना काम किए लगता है तो भी कीवर्ड जोड़ने की सलाह देता हूं । यह भविष्य में होने वाले परिवर्तनों के संबंध में मंशा को स्पष्ट और अधिक मजबूत बनाता है।


ध्यान दें कि किसी फ़ंक्शन का बाह्यरेखा फ़ंक्शन में संदर्भ (लिंक समय पर हल) करते समय इसका कोड इनबिल्ड हो सकता है; यह आंशिक रूप से इनलाइन पुनरावर्ती कार्य का मामला होगा। एक फ़ंक्शन का संकलक द्वारा इसका सिमेंटिक "इनबिल्ट" भी हो सकता है, जो कि कंपाइलर साइड इफेक्ट्स को मानता है और परिणाम इसके साइड कोड के अनुसार संभावित साइड इफेक्ट्स और परिणाम के भीतर होता है, जबकि अभी भी इसे इनलाइन नहीं कर रहा है। यह "प्रभावी वन डेफिनिशन रूल" पर आधारित है जिसमें कहा गया है कि एक इकाई की सभी परिभाषाएं प्रभावी रूप से समतुल्य होंगी (यदि बिल्कुल समान नहीं हैं)।
जिज्ञासु

किसी फ़ंक्शन द्वारा किसी कॉल (या उसके शब्दार्थ के "इनलाइनिंग) को इनलाइन करने से बचना, जिसका बॉडी कंपाइलर (यहां तक ​​कि ग्लोबल ऑप्टिमाइज़ेशन के साथ लिंक टाइम पर) दिखाई देता है, एक volatileयोग्य फ़ंक्शन पॉइंटर का उपयोग करके संभव है :void (* volatile fun_ptr)() = fun; fun_ptr();
जिज्ञासु

2

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

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

दुर्भाग्य से, यह पहचानने के बजाय कि प्रोग्रामर को किन गारंटियों की आवश्यकता होगी, कई कंपाइलरों ने मानक द्वारा अनिवार्य नंगे न्यूनतम गारंटी की पेशकश करने के बजाय चुना है। यह volatileजितना होना चाहिए उससे बहुत कम उपयोगी है। उदाहरण के लिए, gcc या क्लैंग पर, एक प्रोग्रामर को एक बेसिक "हैंड-ऑफ म्यूटेक्स" को लागू करने की आवश्यकता होती है [एक ऐसा कार्य जहाँ एक म्यूटेक्स को अधिग्रहित और जारी किया जाता है, तब तक ऐसा दोबारा नहीं होगा जब तक कि दूसरा कार्य ऐसा न हो जाए] चार चीजों में से:

  1. एक फ़ंक्शन में म्यूटेक्स के अधिग्रहण और रिलीज को डालें जो कंपाइलर इनलाइन नहीं कर सकता है, और जिसके लिए यह संपूर्ण प्रोग्राम ऑप्टिमाइज़ेशन लागू नहीं कर सकता है।

  2. सभी वस्तुओं को म्यूटेक्स द्वारा संरक्षित करें - जैसे volatileकि यह आवश्यक नहीं होना चाहिए यदि सभी एक्सेस म्यूटेक्स प्राप्त करने और इसे जारी करने से पहले हो।

  3. कंपाइलर को कोड उत्पन्न करने के लिए बाध्य करने के लिए ऑप्टिमाइज़ेशन स्तर 0 का उपयोग करें, हालांकि सभी ऑब्जेक्ट जो योग्य नहीं registerहैं volatile

  4. जीसीसी-विशिष्ट निर्देशों का उपयोग करें।

इसके विपरीत, जब उच्च-गुणवत्ता वाले संकलक का उपयोग किया जाता है, जो सिस्टम प्रोग्रामिंग के लिए अधिक उपयुक्त होता है, जैसे कि icc, तो एक और विकल्प होगा:

  1. सुनिश्चित करें कि एक volatileअयोग्य लेख हर जगह एक अधिग्रहण या रिलीज की जरूरत हो जाता है।

एक बुनियादी "हैंड-ऑफ म्यूटेक्स" को प्राप्त करने के लिए एक volatileरीड की आवश्यकता होती है (यह देखने के लिए कि क्या यह तैयार है), और इसे volatileलिखने के साथ ही आवश्यकता नहीं होनी चाहिए (दूसरा पक्ष इसे तब तक पुनः प्राप्त करने की कोशिश नहीं करेगा, जब तक कि इसे वापस नहीं दिया जाता) लेकिन होने अर्थहीन volatileलेखन करना अभी भी gcc या clang के तहत उपलब्ध किसी भी विकल्प से बेहतर है।


1

एक उपयोग जो मुझे आपको याद दिलाना चाहिए, सिग्नल हैंडलर फ़ंक्शन में, यदि आप एक वैश्विक चर को एक्सेस / संशोधित करना चाहते हैं (उदाहरण के लिए, इसे बाहर निकलें = सच के रूप में चिह्नित करें) आपको उस चर को 'अस्थिर' घोषित करना होगा।


1

सभी उत्तर उत्कृष्ट हैं। लेकिन उसके शीर्ष पर, मैं एक उदाहरण साझा करना चाहूंगा।

नीचे एक छोटा सा कार्यक्रम है:

#include <iostream>

int x;

int main(){
    char buf[50];
    x = 8;

    if(x == 8)
        printf("x is 8\n");
    else
        sprintf(buf, "x is not 8\n");

    x=1000;
    while(x > 5)
        x--;
    return 0;
}

अब, उपरोक्त कोड की असेंबली को जेनरेट करने देता है (और मैं असेंबली के केवल वही हिस्से पेस्ट करूंगा जो यहां प्रासंगिक हैं):

विधानसभा बनाने की आज्ञा:

g++ -S -O3 -c -fverbose-asm -Wa,-adhln assembly.cpp

और विधानसभा:

main:
.LFB1594:
    subq    $40, %rsp    #,
    .seh_stackalloc 40
    .seh_endprologue
 # assembly.cpp:5: int main(){
    call    __main   #
 # assembly.cpp:10:         printf("x is 8\n");
    leaq    .LC0(%rip), %rcx     #,
 # assembly.cpp:7:     x = 8;
    movl    $8, x(%rip)  #, x
 # assembly.cpp:10:         printf("x is 8\n");
    call    _ZL6printfPKcz.constprop.0   #
 # assembly.cpp:18: }
    xorl    %eax, %eax   #
    movl    $5, x(%rip)  #, x
    addq    $40, %rsp    #,
    ret 
    .seh_endproc
    .p2align 4,,15
    .def    _GLOBAL__sub_I_x;   .scl    3;  .type   32; .endef
    .seh_proc   _GLOBAL__sub_I_x

आप असेंबली में देख सकते हैं कि असेंबली कोड sprintfइसलिए जेनरेट नहीं किया गया था क्योंकि कंपाइलर ने मान लिया था कि xवह प्रोग्राम के बाहर नहीं बदलेगा। और whileलूप के मामले में भी ऐसा ही है । whileअनुकूलन के कारण लूप को पूरी तरह से हटा दिया गया क्योंकि संकलक ने इसे एक बेकार कोड के रूप में देखा और इस तरह सीधे (देखें ) 5को सौंपा ।xmovl $5, x(%rip)

समस्या तब होती है जब कोई बाहरी प्रक्रिया / हार्डवेयर xकहीं x = 8;और के बीच के मूल्य को बदल देता है if(x == 8)। हम elseकाम करने के लिए ब्लॉक की उम्मीद करेंगे लेकिन दुर्भाग्य से संकलक ने उस हिस्से को छंटनी कर दी है।

अब, इसे हल करने के लिए assembly.cpp, हमें विधानसभा कोड को जेनरेट int x;करने के लिए volatile int x;जल्दी से बदल कर देखें:

main:
.LFB1594:
    subq    $104, %rsp   #,
    .seh_stackalloc 104
    .seh_endprologue
 # assembly.cpp:5: int main(){
    call    __main   #
 # assembly.cpp:7:     x = 8;
    movl    $8, x(%rip)  #, x
 # assembly.cpp:9:     if(x == 8)
    movl    x(%rip), %eax    # x, x.1_1
 # assembly.cpp:9:     if(x == 8)
    cmpl    $8, %eax     #, x.1_1
    je  .L11     #,
 # assembly.cpp:12:         sprintf(buf, "x is not 8\n");
    leaq    32(%rsp), %rcx   #, tmp93
    leaq    .LC0(%rip), %rdx     #,
    call    _ZL7sprintfPcPKcz.constprop.0    #
.L7:
 # assembly.cpp:14:     x=1000;
    movl    $1000, x(%rip)   #, x
 # assembly.cpp:15:     while(x > 5)
    movl    x(%rip), %eax    # x, x.3_15
    cmpl    $5, %eax     #, x.3_15
    jle .L8  #,
    .p2align 4,,10
.L9:
 # assembly.cpp:16:         x--;
    movl    x(%rip), %eax    # x, x.4_3
    subl    $1, %eax     #, _4
    movl    %eax, x(%rip)    # _4, x
 # assembly.cpp:15:     while(x > 5)
    movl    x(%rip), %eax    # x, x.3_2
    cmpl    $5, %eax     #, x.3_2
    jg  .L9  #,
.L8:
 # assembly.cpp:18: }
    xorl    %eax, %eax   #
    addq    $104, %rsp   #,
    ret 
.L11:
 # assembly.cpp:10:         printf("x is 8\n");
    leaq    .LC1(%rip), %rcx     #,
    call    _ZL6printfPKcz.constprop.1   #
    jmp .L7  #
    .seh_endproc
    .p2align 4,,15
    .def    _GLOBAL__sub_I_x;   .scl    3;  .type   32; .endef
    .seh_proc   _GLOBAL__sub_I_x

यहाँ आप देख सकते हैं कि के लिए विधानसभा कोड sprintf, printfऔर whileपाश उत्पन्न किया गया। लाभ यह है कि अगर xचर को किसी बाहरी प्रोग्राम या हार्डवेयर द्वारा बदल दिया जाता है, sprintfतो कोड का हिस्सा निष्पादित किया जाएगा। और इसी तरह whileलूप का उपयोग अब व्यस्त प्रतीक्षा के लिए किया जा सकता है।


0

अन्य उत्तर पहले से ही कुछ अनुकूलन से बचने का उल्लेख करते हैं:

  • मेमोरी मैप्ड रजिस्टरों (या "MMIO") का उपयोग करें
  • डिवाइस ड्राइवर लिखें
  • कार्यक्रमों के आसान डिबगिंग की अनुमति दें
  • फ़्लोटिंग पॉइंट कंप्यूटेशन को अधिक निर्धारक बनाते हैं

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

एक वाष्पशील रीड एक इनपुट ऑपरेशन की तरह है (जैसे scanfया एक उपयोग cin): मान प्रोग्राम के बाहर से आता है, इसलिए किसी भी संगणना जिस पर मूल्य पर निर्भरता है, उसके बाद शुरू होने की आवश्यकता है

एक वाष्पशील लेखन एक आउटपुट ऑपरेशन की तरह है (जैसे printfया एक उपयोग cout): मान प्रोग्राम के बाहर संचारित होने लगता है, इसलिए यदि मान एक संगणना पर निर्भर करता है, तो इसे पहले समाप्त करने की आवश्यकता होती है

तो अस्थिर पढ़ने / लिखने की एक जोड़ी का उपयोग बेंचमार्क को वश में करने और समय मापन को सार्थक बनाने के लिए किया जा सकता है

अस्थिरता के बिना, आपकी गणना कंपाइलर द्वारा पहले शुरू की जा सकती थी, क्योंकि समय मापन जैसे कार्यों के साथ कुछ भी गणना की पुनरावृत्ति को नहीं रोक सकेगा

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.