FCFS ও SJF শিডিউলিং
এই পাঠে যা শিখবেন
- FCFS অ্যালগরিদম এবং convoy effect কীভাবে ঘটে
- SJF অ্যালগরিদম এবং কেন এটি অপ্টিমাল কিন্তু ব্যবহারিকভাবে সীমাবদ্ধ
- SRTF (preemptive SJF variant) সংক্ষিপ্ত পরিচিতি
- Python দিয়ে দুটি real শিডিউলার — একই প্রসেস সেটে waiting/turnaround time গণনা করে সরাসরি তুলনা
১ · FCFS (First-Come, First-Served)
FCFS সবচেয়ে সহজ শিডিউলিং অ্যালগরিদম — প্রসেসরা তাদের arrival time-এর ক্রমে সারিবদ্ধভাবে চলে, কোনো reordering হয় না, এবং এটি non-preemptive (L09) — একবার একটি প্রসেস CPU পেলে তা সম্পূর্ণ শেষ না হওয়া পর্যন্ত ধরে রাখে।
সমস্যা হলো Convoy EffectConvoy effectএকটি দীর্ঘ CPU-burst প্রসেস সামনে থাকলে তার পেছনের সব (এমনকি খুব ছোট) প্রসেসকেও দীর্ঘ সময় অপেক্ষা করতে হয়। — যদি একটি দীর্ঘ প্রসেস সামনে থাকে, তার পেছনে থাকা প্রতিটি প্রসেসকে (তারা যত ছোটই হোক না কেন) সেই দীর্ঘ প্রসেসটি শেষ হওয়া পর্যন্ত অপেক্ষা করতে হয় — গড় waiting time খারাপের দিকে ঠেলে দেয়।
২ · SJF (Shortest Job First)
SJF প্রতিটি সিদ্ধান্তের মুহূর্তে READY প্রসেসগুলোর মধ্যে যেটির মোট CPU burst time সবচেয়ে কম, সেটিকে চালায়। একটি গুরুত্বপূর্ণ তাত্ত্বিক ফলাফল — non-preemptive শিডিউলিং অ্যালগরিদমগুলোর মধ্যে SJF গড় waiting time সর্বনিম্ন করার জন্য প্রমাণিতভাবে অপ্টিমাল। কিন্তু এর একটি গুরুত্বপূর্ণ ব্যবহারিক সীমাবদ্ধতা আছে — এটি কাজ করার জন্য প্রতিটি প্রসেসের burst time আগে থেকে জানা থাকতে হয়, যা বাস্তব সিস্টেমে সাধারণত শুধুই অনুমান করা যায় (আগের বার্স্টের গড় থেকে), সঠিকভাবে জানা যায় না।
একটি preemptive variantও আছে — SRTF (Shortest Remaining Time First) — যেখানে একটি নতুন-আগত প্রসেসের remaining time যদি বর্তমানে-চলমান প্রসেসের চেয়ে কম হয়, তাহলে সেটি চলমান প্রসেসকে preempt করতে পারে (সংক্ষিপ্ত উল্লেখ, বিস্তারিত এই কোর্সে আলাদাভাবে কভার করা হয়নি)।
$n$টি প্রসেসের জন্য গড় waiting time:
$$\text{Average Waiting Time} = \frac{1}{n}\sum_{i=1}^{n} W_i$$যেখানে $W_i$ হলো $i$-তম প্রসেসের waiting time (CPU পাওয়ার আগে READY কিউতে কাটানো সময়)। নিচের কোড সেলে FCFS ও SJF উভয়ের জন্য এই একই সূত্র প্রয়োগ করে প্রকৃত সংখ্যা গণনা করা হবে।
৩ · FCFS বনাম SJF — real কোডে গণনা
নিচের কোড সেলে ৪টি প্রসেস (arrival_time, burst_time সহ) নিয়ে FCFS ও SJF উভয় শিডিউলার বাস্তবায়ন করা হয়েছে — লক্ষ্য করুন প্রসেসগুলো ইচ্ছাকৃতভাবে এমনভাবে সাজানো যাতে সবচেয়ে ছোট burst-এর প্রসেসটি (P3) arrival-এর ক্রমে সবার আগে নেই, তাই দুই অ্যালগরিদমের পার্থক্য স্পষ্টভাবে দেখা যাবে।
# FCFS ও SJF শিডিউলার -- সত্যিকারের অ্যালগরিদম, টয় প্রসেস ডেটার উপর, কোনো real OS কল নয়
processes = [
# (নাম, arrival_time, burst_time)
("P1", 0, 7),
("P2", 1, 2),
("P3", 2, 1),
("P4", 3, 4),
]
def fcfs(processes):
procs = sorted(processes, key=lambda p: p[1]) # arrival অনুযায়ী ক্রম
time = 0
results = []
for name, arrival, burst in procs:
start = max(time, arrival)
completion = start + burst
waiting = start - arrival
turnaround = completion - arrival
time = completion
results.append((name, arrival, burst, start, completion, waiting, turnaround))
return results
def sjf_nonpreemptive(processes):
remaining = list(processes)
time = 0
results = []
done_names = set()
while len(done_names) < len(remaining):
available = [p for p in remaining if p[1] <= time and p[0] not in done_names]
if not available:
future = [p for p in remaining if p[0] not in done_names]
time = min(p[1] for p in future) # পরবর্তী arrival পর্যন্ত এগিয়ে যাওয়া
continue
chosen = min(available, key=lambda p: p[2]) # সবচেয়ে ছোট burst
name, arrival, burst = chosen
start = time
completion = start + burst
waiting = start - arrival
turnaround = completion - arrival
time = completion
results.append((name, arrival, burst, start, completion, waiting, turnaround))
done_names.add(name)
return results
def print_schedule(title, results):
print(f"--- {title} ---")
print(f"{'নাম':4}{'arrival':9}{'burst':7}{'start':7}{'completion':12}{'waiting':9}{'turnaround':10}")
for name, arrival, burst, start, completion, waiting, turnaround in results:
print(f"{name:4}{arrival:<9}{burst:<7}{start:<7}{completion:<12}{waiting:<9}{turnaround:<10}")
avg_wait = sum(r[5] for r in results) / len(results)
avg_turn = sum(r[6] for r in results) / len(results)
print(f"গড় waiting time = {avg_wait:.2f}")
print(f"গড় turnaround time = {avg_turn:.2f}\n")
return avg_wait
fcfs_results = fcfs(processes)
avg_wait_fcfs = print_schedule("FCFS", fcfs_results)
sjf_results = sjf_nonpreemptive(processes)
avg_wait_sjf = print_schedule("SJF (non-preemptive)", sjf_results)
print("=== তুলনা ===")
print(f"FCFS গড় waiting time = {avg_wait_fcfs:.2f}")
print(f"SJF গড় waiting time = {avg_wait_sjf:.2f}")
print("SJF <= FCFS (গড় waiting time)?", avg_wait_sjf <= avg_wait_fcfs)
SJF তাত্ত্বিকভাবে অপ্টিমাল হলেও এর ব্যবহারিক প্রয়োগ নির্ভর করে burst time নির্ভুলভাবে অনুমান করার উপর — একটি বাস্তব সীমাবদ্ধতা যা L12-এর মাল্টিলেভেল ফিডব্যাক কিউ এড়িয়ে যায় (future burst time না জেনেই অতীত আচরণের ভিত্তিতে অভিযোজিত হয়ে)।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ উপরের কোড সেলে P1-এর waiting time FCFS ও SJF উভয়েতেই ০ কেন — SJF কি P1-কে কোনোভাবেই প্রভাবিত করে না?
P1 সময় ০-এ আসে এবং একমাত্র READY প্রসেস হিসেবে অবিলম্বে CPU পায় — কোনো তুলনা করার সুযোগই নেই কারণ তখন অন্য কোনো প্রসেস READY নয়। SJF শুধু তখনই পার্থক্য তৈরি করে যখন একাধিক প্রসেস একসাথে READY থাকে এবং বাছাই করার প্রয়োজন হয় — যা P1 সম্পূর্ণ হওয়ার (সময় ৭) পরে P2, P3, P4-এর মধ্যে ঘটে।
প্র ০২ যদি সব প্রসেসের burst time সমান হতো, FCFS ও SJF-এর গড় waiting time-এ কোনো পার্থক্য থাকতো কি?
না — যদি সব burst time সমান হয়, SJF-এর "সবচেয়ে ছোট আগে" নিয়মটি কোনো নির্দিষ্ট ক্রম বেছে নেওয়ার কারণ দেয় না (সব সমান হলে যেকোনো ক্রমই "সবচেয়ে ছোট" নির্বাচন করার সমতুল্য), ফলে উভয় অ্যালগরিদম কার্যত একই ফলাফল দেবে (arrival অনুযায়ী)। SJF-এর সুবিধা তখনই প্রকাশ পায় যখন burst time-এ বৈচিত্র্য থাকে।
প্র ০৩ SJF বাস্তবে কেন সরাসরি ব্যবহার করা কঠিন, এবং একটি সিস্টেম কীভাবে burst time "অনুমান" করতে পারে?
বাস্তবে কোনো প্রসেস চালানোর আগে তার CPU burst time নিশ্চিতভাবে জানা যায় না — এটি ইনপুট ডেটা, শাখা সিদ্ধান্ত ইত্যাদির উপর নির্ভর করে। একটি সাধারণ কৌশল হলো প্রসেসের অতীত bursts-এর একটি exponential moving average রাখা এবং তা দিয়ে পরবর্তী burst অনুমান করা — এই অনুমানকৃত মান দিয়েই SJF/SRTF সিদ্ধান্ত নেওয়া হয়, প্রকৃত ভবিষ্যৎ মান দিয়ে নয়।
অনুশীলন
-
চিন্তা করুন: convoy effect বাস্তব জীবনে সুপারমার্কেটের একটি ক্যাশ কাউন্টারের লাইনের সাথে কীভাবে তুলনীয়, যেখানে একজন ক্রেতার কার্টে ১০০টি জিনিস আর তার পেছনের জনের মাত্র ২টি জিনিস?
ঠিক যেমন FCFS-এ একটি দীর্ঘ CPU burst-এর প্রসেস পেছনের ছোট প্রসেসদের অপেক্ষা করায়, একজন ১০০-আইটেমের ক্রেতা ক্যাশ কাউন্টারে থাকলে ২-আইটেমের ক্রেতাকেও পুরো ১০০-আইটেম চেকআউট শেষ হওয়া পর্যন্ত অপেক্ষা করতে হয় — অনেক সুপারমার্কেটে "express lane" (কম আইটেমের জন্য আলাদা লাইন) ঠিক SJF-এর মতোই একটি বাস্তব সমাধান।
-
পরীক্ষা করুন: উপরের কোড সেলে
processesতালিকায় P3-এর burst_time 1 থেকে 10 করে দিন (এখন এটি সবচেয়ে বড় burst)। FCFS ও SJF-এর গড় waiting time-এর সম্পর্ক কি এখনো একই থাকবে?P3-এর burst 10 করলে P4 (burst 4) এখন সবচেয়ে ছোট হয়ে যাবে, তাই SJF এখন P1 শেষ হওয়ার পর P2, তারপর P4, তারপর P3 চালাবে (ক্রম বদলে যাবে) — কিন্তু SJF তাত্ত্বিকভাবে অপ্টিমাল থাকার প্রমাণ যেকোনো burst time কম্বিনেশনের জন্যই প্রযোজ্য, তাই SJF-এর গড় waiting time এখনো FCFS-এর সমান অথবা কম-ই থাকবে, শুধু নির্দিষ্ট সংখ্যাগুলো বদলে যাবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরের পাঠ — প্রায়োরিটি শিডিউলিং ও Round Robin L11 SJF-এর "burst time জানতে হয়" সমস্যা এড়িয়ে কীভাবে অন্য দুটি জনপ্রিয় অ্যালগরিদম কাজ করে তা দেখুন।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ M3-এর বাকি পাঠগুলো (L11-L13) আরও শিডিউলিং অ্যালগরিদম ও মাল্টিপ্রসেসর কেস কভার করবে।
- Discrete Mathematics কোর্স সঙ্গী কোর্স অ্যালগরিদমের optimality প্রমাণ ও average-case বিশ্লেষণের গাণিতিক ভিত্তি আরও গভীরভাবে শিখতে দেখুন।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks ও Operating Systems — সব এক জায়গায়।