পেজ রিপ্লেসমেন্ট অ্যালগরিদম — FIFO ও Optimal
এই পাঠে যা শিখবেন
- পেজ রিপ্লেসমেন্ট অ্যালগরিদমের প্রয়োজন কেন হয় এবং একটি "রেফারেন্স স্ট্রিং" কী বোঝায়
- FIFO অ্যালগরিদম ধাপে ধাপে, এবং Belady's Anomaly-এর মতো একটি কাউন্টার-ইনটুইটিভ সমস্যা
- Optimal (OPT) অ্যালগরিদম কীভাবে কাজ করে এবং কেন এটি শুধুই একটি তাত্ত্বিক বেঞ্চমার্ক
- Python-এ দুটো অ্যালগরিদম বাস্তবায়ন করে একই ইনপুটে তুলনা করা — সংখ্যাগুলো নিজে গণনা করে দেখা
১ · পেজ রিপ্লেসমেন্ট কেন দরকার
L32-এ আমরা দেখেছি ডিমান্ড পেজিং-এ একটি পেজ ফল্ট ঘটলে OS একটি ফ্রি ফ্রেম খুঁজে সেখানে পেজ লোড করে। কিন্তু বাস্তবে ফিজিক্যাল মেমরি সীমিত — একসময় সব ফ্রেম ভরে যায়। তখন নতুন একটি পেজ ফল্ট ঘটলে OS-কে একটি সিদ্ধান্ত নিতে হয়: বর্তমানে মেমরিতে থাকা কোন পেজটিকে সরিয়ে (evict) নতুন পেজের জন্য জায়গা করা হবে? এই সিদ্ধান্ত নেওয়ার নিয়মটাই পেজ রিপ্লেসমেন্ট অ্যালগরিদম (Page Replacement Algorithm)Page Replacement Algorithmমেমরি পূর্ণ থাকা অবস্থায় একটি নতুন পেজ ফল্ট হলে বর্তমানে মেমরিতে থাকা কোন পেজটি সরানো হবে তা ঠিক করার নিয়ম।। একটি খারাপ পছন্দ ভবিষ্যতে আরও বেশি পেজ ফল্ট ঘটাতে পারে — তাই এই অ্যালগরিদমগুলোর কার্যকারিতা সরাসরি সামগ্রিক সিস্টেম পারফরম্যান্সে প্রভাব ফেলে (সরাসরি L32-এর Effective Access Time আলোচনার সাথে যুক্ত)।
এই পাঠ ও পরের পাঠে (L34) আমরা একটি নির্দিষ্ট রেফারেন্স স্ট্রিং (পেজ অ্যাক্সেসের ক্রম) ও নির্দিষ্ট সংখ্যক ফ্রেম ব্যবহার করব যাতে তিনটি অ্যালগরিদমের (FIFO, Optimal, LRU) ফল্ট সংখ্যা সরাসরি তুলনাযোগ্য হয়।
২ · FIFO (First-In, First-Out)
FIFOFirst-In, First-Outযে পেজ সবচেয়ে বেশিদিন ধরে মেমরিতে আছে (সবার আগে ঢুকেছিল) তাকে সরিয়ে দেওয়ার নিয়ম, তার সাম্প্রতিক ব্যবহার বিবেচনা না করেই। হলো সবচেয়ে সরল রিপ্লেসমেন্ট অ্যালগরিদম — একটি সাধারণ queue দিয়েই বাস্তবায়ন করা যায়। যে পেজ সবার আগে মেমরিতে ঢুকেছিল, নতুন জায়গা দরকার হলে তাকেই সরানো হয় — সেই পেজ কতবার বা কত সম্প্রতি ব্যবহৃত হয়েছে তা একেবারেই বিবেচনা করা হয় না।
৩ · Optimal (OPT) — তাত্ত্বিক সেরা
Optimal (OPT)Optimal Page Replacementযে পেজ ভবিষ্যতে সবচেয়ে দেরিতে ব্যবহৃত হবে (বা আর কখনোই ব্যবহৃত হবে না) তাকে সরানোর নিয়ম — প্রমাণিতভাবে যেকোনো রেফারেন্স স্ট্রিং ও ফ্রেম সংখ্যার জন্য সর্বনিম্ন সম্ভাব্য ফল্ট দেয়। সরায় ঠিক সেই পেজ যেটি ভবিষ্যতে সবচেয়ে দূরে আবার ব্যবহৃত হবে — বা আর কখনোই ব্যবহৃত হবে না। এটি প্রমাণিতভাবে যেকোনো রেফারেন্স স্ট্রিং ও ফ্রেম সংখ্যার জন্য সর্বনিম্ন সম্ভাব্য পেজ-ফল্ট সংখ্যা দেয়। সমস্যা হলো — এটি বাস্তবায়ন করতে ভবিষ্যতের অ্যাক্সেস প্যাটার্ন আগে থেকে জানা লাগে, যা একটি সত্যিকারের চলমান সিস্টেমে অসম্ভব। তাই Optimal শুধু একটি তাত্ত্বিক বেঞ্চমার্ক হিসেবে ব্যবহৃত হয় — বাস্তব বাস্তবায়নযোগ্য অ্যালগরিদম (যেমন L34-এর LRU) ঠিক কতটা ভালো/খারাপ পারফর্ম করছে তা মাপার জন্য।
বাস্তবায়নযোগ্য, সরল — কিন্তু ব্যবহারের ধরন উপেক্ষা করে, Belady's Anomaly-প্রবণ।
তাত্ত্বিকভাবে সর্বোত্তম — কিন্তু ভবিষ্যৎ জানা লাগে বলে বাস্তবে বাস্তবায়নযোগ্য নয়, শুধু বেঞ্চমার্ক।
৪ · কোডে যাচাই: একই রেফারেন্স স্ট্রিং-এ FIFO বনাম Optimal
নিচের কোড সেলে FIFO এবং Optimal — দুটোই বাস্তব অ্যালগরিদম হিসেবে বাস্তবায়ন করা হয়েছে (হার্ডকোড করা ফলাফল নয়) এবং একই রেফারেন্স স্ট্রিং ও ৩টি ফ্রেমে চালানো হয়েছে — যাতে ফলাফল সরাসরি তুলনাযোগ্য হয়। এই ঠিক এই রেফারেন্স স্ট্রিং ও ফ্রেম সংখ্যা L34-এ LRU-এর সাথে তুলনার জন্যও ব্যবহৃত হবে।
# পেজ রিপ্লেসমেন্ট: FIFO ও Optimal -- বাস্তব অ্যালগরিদম, টয় রেফারেন্স স্ট্রিং-এর ওপর
# পুরো M8 জুড়ে একই রেফারেন্স স্ট্রিং ও ফ্রেম সংখ্যা ব্যবহার করা হবে (L34-এর LRU-এর সাথে তুলনার জন্য)
REFERENCE_STRING = [7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1]
NUM_FRAMES = 3
def fifo(reference_string, num_frames):
frames = [] # queue -- সবচেয়ে পুরনো পেজ index 0-এ
faults = 0
for page in reference_string:
if page in frames:
continue # HIT
faults += 1 # FAULT
if len(frames) < num_frames:
frames.append(page)
else:
frames.pop(0) # সবচেয়ে পুরনো পেজ সরানো হলো
frames.append(page)
return faults
def optimal(reference_string, num_frames):
frames = []
faults = 0
n = len(reference_string)
for i, page in enumerate(reference_string):
if page in frames:
continue # HIT
faults += 1 # FAULT
if len(frames) < num_frames:
frames.append(page)
else:
future = reference_string[i + 1:]
# প্রতিটি resident পেজ ভবিষ্যতে কতদূরে আবার ব্যবহৃত হবে তা বের করা
farthest_use, evict_idx = -1, 0
for idx, resident_page in enumerate(frames):
next_use = future.index(resident_page) if resident_page in future else n
if next_use > farthest_use:
farthest_use, evict_idx = next_use, idx
frames[evict_idx] = page # সবচেয়ে দেরিতে/কখনো-না-ব্যবহৃত পেজ সরানো হলো
return faults
fifo_faults = fifo(REFERENCE_STRING, NUM_FRAMES)
optimal_faults = optimal(REFERENCE_STRING, NUM_FRAMES)
print("রেফারেন্স স্ট্রিং:", REFERENCE_STRING)
print("ফ্রেম সংখ্যা:", NUM_FRAMES)
print()
print("FIFO মোট পেজ ফল্ট:", fifo_faults)
print("Optimal মোট পেজ ফল্ট:", optimal_faults)
print()
if optimal_faults <= fifo_faults:
print(f"যাচাই সফল -> Optimal ({optimal_faults}) <= FIFO ({fifo_faults})")
else:
print("অপ্রত্যাশিত ফলাফল -- সংখ্যা পুনরায় যাচাই করুন")
Optimal আমাদের বলে দেয় "সর্বোত্তম সম্ভব" ফলাফল কী হতে পারত — একটি লক্ষ্য (target), বাস্তব সমাধান নয়। FIFO বাস্তবায়নযোগ্য কিন্তু ব্যবহারের ধরন সম্পূর্ণ উপেক্ষা করে বলে প্রায়ই Optimal থেকে অনেক দূরে থাকে। পরের পাঠে আমরা দেখব LRU কীভাবে শুধু অতীতের তথ্য ব্যবহার করেও Optimal-এর অনেক কাছাকাছি পৌঁছাতে পারে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ Optimal অ্যালগরিদম "ভবিষ্যৎ জেনে" কাজ করে বলা হচ্ছে কেন — উপরের কোডে তো এটি সিমুলেশনের মধ্যেই চলছে?
এখানে যেহেতু পুরো রেফারেন্স স্ট্রিংটি আগে থেকেই একটি Python লিস্ট হিসেবে সম্পূর্ণ লেখা আছে, কোড
reference_string[i+1:] দিয়ে "ভবিষ্যৎ" দেখে ফেলতে পারছে — এটি সিমুলেশনের একটি সুবিধা। কিন্তু
একটি বাস্তব, চলমান OS-এ প্রসেসগুলো কোন পেজ কখন অ্যাক্সেস করবে তা আগে থেকে জানার কোনো উপায় নেই (ব্যবহারকারীর
পরবর্তী ক্লিক বা প্রোগ্রামের পরবর্তী শাখা কেউ আগে থেকে বলতে পারে না) — তাই বাস্তবে Optimal প্রয়োগ করা যায় না,
এটি শুধু অফলাইন বিশ্লেষণ/বেঞ্চমার্কিংয়ে ব্যবহার্য।
প্র ০২ FIFO-তে যে পেজ সবচেয়ে বেশি ব্যবহৃত হচ্ছে (হট পেজ) সেটিও কি সরানো হয়ে যেতে পারে?
হ্যাঁ, একেবারেই — এটাই FIFO-র সবচেয়ে বড় দুর্বলতা। FIFO শুধু দেখে কোন পেজ সবার আগে মেমরিতে ঢুকেছিল, সেই পেজটি এখনো ঘনঘন ব্যবহৃত হচ্ছে কি না তা একেবারেই বিবেচনা করে না। ফলে একটি গুরুত্বপূর্ণ, বারবার-ব্যবহৃত পেজ শুধু "পুরনো" হওয়ার কারণে সরানো হয়ে যেতে পারে, আর তারপর প্রায় সাথে সাথেই আবার ফল্ট ঘটিয়ে ফিরে আসতে পারে — একটি সত্যিকারের অদক্ষতা যা L34-এর LRU সরাসরি সমাধান করে।
প্র ০৩
কোড সেলে NUM_FRAMES ২টিতে কমালে ফল্ট সংখ্যার ওপর কী প্রভাব পড়বে বলে মনে করেন, এবং কেন?
সাধারণত ফ্রেম কম হলে ফল্ট সংখ্যা বেড়ে যাওয়ার কথা — কম জায়গা মানে পেজগুলো বেশি ঘন ঘন সরানো হবে। FIFO এবং
Optimal দুটোতেই এটি প্রায় সবসময় সত্য, যদিও FIFO-র ক্ষেত্রে Belady's Anomaly-এর মতো ব্যতিক্রমী রেফারেন্স
স্ট্রিং থাকতে পারে যেখানে ফ্রেম বাড়ালে উল্টো ফল্ট বাড়ে। নিজে NUM_FRAMES = 2 বসিয়ে Run করে
দেখুন প্রকৃত সংখ্যাটি কী দাঁড়ায়।
অনুশীলন
-
পরীক্ষা করুন: উপরের কোড সেলে
NUM_FRAMES-কে 3 থেকে 4 করে Run চেপে দেখুন FIFO ও Optimal-এর ফল্ট সংখ্যা কীভাবে বদলায়।বেশি ফ্রেম থাকলে সাধারণত দুটো অ্যালগরিদমেরই ফল্ট সংখ্যা কমার কথা (বেশি পেজ একসাথে মেমরিতে রাখা যায়)। সংখ্যাটি ঠিক কত হলো তা কোড চালিয়েই দেখুন — এবং লক্ষ্য করুন Optimal-এর ফল্ট সংখ্যা তখনও FIFO-এর সমান বা তার চেয়ে কম থাকছে কি না, যা Optimal-এর "সর্বোত্তম" দাবিটাকেই সমর্থন করে।
-
চিন্তা করুন: Optimal-এর কোডে
next_use = future.index(resident_page) if resident_page in future else nলাইনটিতে "আর কখনো ব্যবহৃত না হওয়া" পেজকে কীভাবে চিহ্নিত করা হচ্ছে?যদি
resident_pageভবিষ্যতের অংশে (future) একেবারেই না থাকে, তাহলেin futureচেক False হয় এবং কোডn(রেফারেন্স স্ট্রিং-এর মোট দৈর্ঘ্য) বসিয়ে দেয় — যা যেকোনো প্রকৃত ইনডেক্সের চেয়ে বড়, ফলে এই পেজটিকে সবসময় "সবচেয়ে দূরে ব্যবহৃত হবে" হিসেবে গণ্য করা হয় এবং সবার আগে সরানোর জন্য বেছে নেওয়া হয় — ঠিক যেমনটা তাত্ত্বিকভাবে হওয়া উচিত।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- ডিমান্ড পেজিং (L32) আবার দেখুন রিক্যাপ পেজ ফল্ট ও ট্র্যাপ-লোড-রিজিউম চক্রের মূল ধারণাটা ঝালিয়ে নিন।
- পরের পাঠ: পেজ রিপ্লেসমেন্ট — LRU L34 FIFO ও Optimal-এর সাথে LRU-এর সরাসরি তুলনা, একই রেফারেন্স স্ট্রিং ব্যবহার করে।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ M8-এর বাকি পাঠগুলো (LRU, থ্র্যাশিং, ফ্রেম অ্যালোকেশন) এবং পুরো কোর্স।