একটি সংজ্ঞা volatile
volatile
সংকলককে জানায় যে সংকলকটি না জেনে ভেরিয়েবলের মান পরিবর্তন হতে পারে। সুতরাং সংকলকটি ধরে নিতে পারে মানটির কোনও পরিবর্তন হয়নি কারণ কেবলমাত্র সি প্রোগ্রামটি এটি পরিবর্তন করে নি।
অন্যদিকে, এর অর্থ হ'ল ভেরিয়েবলের মান প্রয়োজন (পড়ুন) অন্য কোথাও সংকলকটি জানেন না, সুতরাং এটি অবশ্যই নিশ্চিত করতে হবে যে ভেরিয়েবলের প্রতিটি অ্যাসাইনমেন্ট আসলে রাইটিং অপারেশন হিসাবে সম্পাদিত হয়।
ব্যবহারের ক্ষেত্রে
volatile
যখন প্রয়োজন হয়
- হার্ডওয়্যার রেজিস্টারগুলিকে (বা মেমরি-ম্যাপযুক্ত I / O) ভেরিয়েবল হিসাবে উপস্থাপন করতে হবে - এমনকি রেজিস্টারটি কখনই পড়তে হবে না, সংকলকটি অবশ্যই "বোকা প্রোগ্রামার" ভেবে লেখার ক্রিয়াটি এড়িয়ে চলবে না he তিনি / সে একটি ভেরিয়েবলের মধ্যে একটি মান সংরক্ষণ করার চেষ্টা করে আর কখনও পড়তে হবে না He তিনি লেখার বিষয়টি খেয়ালও করবেন না আমরা লেখাটি বাদ দিই কিনা। " বিপরীতক্রমে, এমনকি যদি প্রোগ্রামটি কখনও ভেরিয়েবলের কাছে কোনও মান না লিখে, তার মানটি এখনও হার্ডওয়্যার দ্বারা পরিবর্তিত হতে পারে।
- এক্সিকিউটিভ প্রসঙ্গে (যেমন আইএসআর / মূল প্রোগ্রাম) এর মধ্যে ভাগ করে নেওয়া পরিবর্তনগুলি (@ কেক্রামোর উত্তর দেখুন)
এর প্রভাব volatile
যখন কোনও ভেরিয়েবল ঘোষিত হয় volatile
তখন অবশ্যই তা নিশ্চিত করতে হবে যে প্রোগ্রাম কোডে এটির প্রতিটি অ্যাসাইনমেন্ট প্রকৃত রাইটিং অপারেশনে প্রতিফলিত হয়েছে এবং প্রোগ্রাম কোডে প্রতিটি পঠিত মান (এমএম্যাপ করা) মেমরি থেকে পড়ে reads
অ-উদ্বায়ী ভেরিয়েবলের জন্য, সংকলকটি ধরে নিচ্ছে যে / যখন ভেরিয়েবলের মান পরিবর্তন হয় এবং কোডটি বিভিন্ন উপায়ে অপ্টিমাইজ করতে পারে তা জানে।
একটির জন্য, সংকলক সিপিইউ রেজিস্টারে মান রেখে মেমরিটিতে পঠন / লেখার সংখ্যা হ্রাস করতে পারে।
উদাহরণ:
void uint8_t compute(uint8_t input) {
uint8_t result = input + 2;
result = result * 2;
if ( result > 100 ) {
result -= 100;
}
return result;
}
এখানে, সংকলক সম্ভবত result
ভেরিয়েবলের জন্য র্যাম বরাদ্দ করবে না এবং মাঝারি মানগুলি আর কোথাও সংরক্ষণ করবে না তবে সিপিইউ রেজিস্টারে থাকবে।
যদি result
অস্থিরতা থাকে result
তবে সি কোডের প্রতিটি ঘটনার জন্য র্যাম (বা একটি আই / ও পোর্ট) অ্যাক্সেস করার জন্য সংকলকটির প্রয়োজন হবে, যার ফলে নিম্ন কার্যকারিতা দেখাবে।
দ্বিতীয়ত, সংকলক পারফরম্যান্স এবং / অথবা কোড আকারের জন্য অ-উদ্বায়ী ভেরিয়েবলগুলির ক্রিয়াকলাপ পুনরায় অর্ডার করতে পারে। সাধারণ উদাহরণ:
int a = 99;
int b = 1;
int c = 99;
পুনরায় আদেশ দেওয়া যেতে পারে
int a = 99;
int c = 99;
int b = 1;
যা কোনও এসেম্বলারের নির্দেশ সংরক্ষণ করতে পারে কারণ মানটি 99
দুটিবার লোড করতে হবে না।
তাহলে a
, b
এবং c
উদ্বায়ী ছিল কম্পাইলার নির্দেশাবলী যা সঠিক অনুক্রমে মান নির্ধারণ হিসাবে তারা কর্মসূচি দেওয়া হয় নির্গত করতে হবে।
অন্যান্য ক্লাসিক উদাহরণটি এরকম:
volatile uint8_t signal;
void waitForSignal() {
while ( signal == 0 ) {
// Do nothing.
}
}
যদি, এই ক্ষেত্রে signal
না হয় volatile
, তবে সংকলকটি 'চিন্তা' করবে যা while( signal == 0 )
একটি অসীম লুপ হতে পারে (কারণ লুপের অভ্যন্তরেsignal
কোড দ্বারা কখনই পরিবর্তন করা যাবে না ) এবং এর সমতুল্য উত্পন্ন করতে পারে
void waitForSignal() {
if ( signal != 0 ) {
return;
} else {
while(true) { // <-- Endless loop!
// do nothing.
}
}
}
volatile
মানগুলি পরিচালনা করা বিবেচনা করুন
উপরে বর্ণিত হিসাবে, কোনও volatile
পরিবর্তনশীল যখন কার্যত প্রয়োজনের চেয়ে বেশি বার অ্যাক্সেস করা হয় তখন একটি পারফরম্যান্স পেনাল্টি প্রবর্তন করতে পারে। এই সমস্যাটি প্রশমিত করতে আপনি একটি অ-উদ্বায়ী ভেরিয়েবলের জন্য বরাদ্দ করে মানটিকে "আন-ভোল্টাইল" করতে পারেন, যেমন
volatile uint32_t sysTickCount;
void doSysTick() {
uint32_t ticks = sysTickCount; // A single read access to sysTickCount
ticks = ticks + 1;
setLEDState( ticks < 500000L );
if ( ticks >= 1000000L ) {
ticks = 0;
}
sysTickCount = ticks; // A single write access to volatile sysTickCount
}
এটি সম্ভবত আইএসআর এর ক্ষেত্রে উপকারী হতে পারে যেখানে আপনি যত তাড়াতাড়ি দ্রুত হতে চান যেখানে একই হার্ডওয়্যার বা মেমরি একাধিকবার অ্যাক্সেস না করে আপনি যখন জানেন যে এটির প্রয়োজন নেই কারণ আপনার আইএসআর চলাকালীন মান পরিবর্তন হবে না। ISR হ'ল sysTickCount
উপরের উদাহরণের মতো চলকগুলির জন্য মানগুলির 'উত্পাদক' হওয়াই এটি সাধারণ । একটি এভিআর এ ফাংশনটি doSysTick()
মেমোরিতে একই চারটি বাইট (অ্যাক্সেস অনুযায়ী চারটি নির্দেশাবলী = 8 সিপিইউ চক্র sysTickCount
) অবিচ্ছিন্নভাবে কেবল দু'বারের পরিবর্তে পাঁচ বা ছয় বার অ্যাক্সেস করা বিশেষত বেদনাদায়ক হবে কারণ প্রোগ্রামার জানেন না যে মানটি হবে না তার doSysTick()
রান চলাকালীন অন্য কোনও কোড থেকে পরিবর্তন করা উচিত ।
এই কৌশলটির সাহায্যে আপনি মূলত কম্পাইলারটি অ-উদ্বায়ী ভেরিয়েবলগুলির জন্য ঠিক একই কাজটি করেন, অর্থাত্ যখন স্মৃতি থেকে পড়তে হয় কেবল তখনই তা পড়ুন, কিছু সময় নিবন্ধকে মান রাখুন এবং যখন মেমরিটিতে ফিরে যেতে হয় কেবল তখনই লিখুন ; তবে এই বার, আপনি / যখন পড়তে / লিখতে হবে তা সংকলকটির চেয়ে ভাল জানেন , সুতরাং আপনি এই অপটিমাইজেশন টাস্ক থেকে সংকলককে মুক্তি দিন এবং নিজেই করুন।
সীমাবদ্ধতা volatile
অ-পরমাণু প্রবেশাধিকার
volatile
মাল্টি-ওয়ার্ড ভেরিয়েবলগুলিতে পারমাণবিক অ্যাক্সেস সরবরাহ করে না । এই ক্ষেত্রে, আপনাকে ব্যবহারের পাশাপাশি অন্য উপায় দ্বারা পারস্পরিক বর্জনীয় সরবরাহ করতে হবে volatile
। এভিআর, আপনি ব্যবহার করতে পারেন ATOMIC_BLOCK
থেকে <util/atomic.h>
বা সহজ cli(); ... sei();
কল। সংশ্লিষ্ট ম্যাক্রোগুলি মেমোরি বাধা হিসাবেও কাজ করে, যা অ্যাক্সেসের ক্রম হিসাবে আসে যখন তা গুরুত্বপূর্ণ:
আদেশ কার্যকর
volatile
কেবলমাত্র অন্যান্য অস্থির ভেরিয়েবলের প্রতি শ্রদ্ধার সাথে কঠোর মৃত্যুদণ্ডের আদেশ চাপায়। এর অর্থ এটি উদাহরণস্বরূপ
volatile int i;
volatile int j;
int a;
...
i = 1;
a = 99;
j = 2;
প্রথমে 1 বরাদ্দ i
এবং তারপরে 2 থেকে 2 প্রদানের গ্যারান্টিযুক্ত j
। কিন্তু, ব্যাপারটা না নিশ্চিত যে a
মধ্যবর্তী নিয়োগ করা হবে; সংকলক কোড স্নিপেটের আগে বা পরে সেই কাজটি করতে পারে, মূলত যে কোনও সময়ে প্রথম (দৃশ্যমান) পড়ার আগে পর্যন্ত a
।
যদি এটি উল্লিখিত ম্যাক্রোগুলির মেমরি বাধা না থাকে তবে সংকলকটিকে অনুবাদ করার অনুমতি দেওয়া হত
uint32_t x;
cli();
x = volatileVar;
sei();
প্রতি
x = volatileVar;
cli();
sei();
অথবা
cli();
sei();
x = volatileVar;
(সম্পূর্ণতার স্বার্থে আমি যে মেমরি বাধা, Sei / CLI ম্যাক্রো ক্ষেত্রে প্রযোজ্য মত বলতে হবে, আসলে ব্যবহার নিবারণ করতে পারে volatile
, যদি সব ব্যবহারের এই বাধা দিয়ে বন্ধনী করা হয়।)