SJF Scheduling β Shortest Job First (Preemptive and Non-Preemptive)
- SJF non-preemptive: when CPU free, pick shortest burst among arrived processes.
- SRTF (preemptive): preempt when new process has shorter remaining time than current.
- SJF/SRTF minimise average waiting time β provably optimal.
SJF is provably optimal for average waiting time among all non-preemptive scheduling algorithms β and SRTF is optimal among all algorithms including preemptive ones. This optimality is not a heuristic or approximation; it follows directly from an exchange argument. If any solution does not follow SJF order, you can swap adjacent jobs to improve average waiting time.
The practical problem: burst times are not known in advance. Linux's CFS (Completely Fair Scheduler) approximates SJF by tracking vruntime β the virtual runtime each process has accumulated β and always scheduling the process with the smallest vruntime. It is SJF-like without requiring burst time prediction, at the cost of true optimality.
Non-Preemptive SJF
When the CPU becomes free, pick the arrived process with the shortest burst time.
import heapq def sjf_non_preemptive(processes: list[dict]) -> list[dict]: procs = sorted(processes, key=lambda p: p['arrival']) results = [] current_time = 0 heap = [] # (burst, arrival, process) i = 0 while i < len(procs) or heap: # Add all processes that have arrived while i < len(procs) and procs[i]['arrival'] <= current_time: heapq.heappush(heap, (procs[i]['burst'], procs[i]['arrival'], procs[i])) i += 1 if not heap: current_time = procs[i]['arrival'] continue burst, arrival, p = heapq.heappop(heap) start = current_time ct = start + p['burst'] tat = ct - p['arrival'] wt = tat - p['burst'] current_time = ct results.append({**p, 'start': start, 'ct': ct, 'tat': tat, 'wt': wt}) return results processes = [ {'pid':'P1','arrival':0,'burst':8}, {'pid':'P2','arrival':1,'burst':4}, {'pid':'P3','arrival':2,'burst':2}, {'pid':'P4','arrival':3,'burst':1}, ] results = sjf_non_preemptive(processes) for r in results: print(f"{r['pid']}: WT={r['wt']}, TAT={r['tat']}") print(f"Avg WT: {sum(r['wt'] for r in results)/len(results):.2f}")
P4: WT=7, TAT=8
P3: WT=8, TAT=10
P2: WT=11, TAT=15
Avg WT: 6.50
Preemptive SJF β SRTF
Shortest Remaining Time First preempts the running process when a new process arrives with shorter remaining time.
def srtf(processes: list[dict]) -> list[dict]: """Shortest Remaining Time First β preemptive SJF.""" procs = [dict(p, remaining=p['burst'], start=-1) for p in processes] procs.sort(key=lambda p: p['arrival']) n = len(procs) time = 0 completed = 0 current = None results = {} while completed < n: # Find process with shortest remaining time among arrived arrived = [p for p in procs if p['arrival'] <= time and p['remaining'] > 0] if not arrived: time += 1 continue current = min(arrived, key=lambda p: p['remaining']) if current['start'] == -1: current['start'] = time current['remaining'] -= 1 time += 1 if current['remaining'] == 0: ct = time tat = ct - current['arrival'] wt = tat - current['burst'] results[current['pid']] = {**current,'ct':ct,'tat':tat,'wt':wt} completed += 1 return list(results.values()) results = srtf(processes) for r in results: print(f"{r['pid']}: WT={r['wt']}, TAT={r['tat']}") print(f"Avg WT: {sum(r['wt'] for r in results)/len(results):.2f}")
P2: WT=1, TAT=5
P3: WT=0, TAT=2
P4: WT=0, TAT=1
Avg WT: 2.50
Why SJF is Optimal
SJF minimises average waiting time among all non-preemptive algorithms. Proof by exchange argument: if a longer job runs before a shorter one, swapping them reduces total waiting time. SRTF minimises average waiting time among ALL scheduling algorithms (preemptive and non-preemptive).
Burst Time Prediction
Real schedulers don't know burst times in advance. Prediction via exponential averaging: Ο_{n+1} = Ξ± Γ t_n + (1-Ξ±) Γ Ο_n where t_n is actual burst time, Ο_n is predicted, Ξ± controls responsiveness (typically 0.5).
π― Key Takeaways
- SJF non-preemptive: when CPU free, pick shortest burst among arrived processes.
- SRTF (preemptive): preempt when new process has shorter remaining time than current.
- SJF/SRTF minimise average waiting time β provably optimal.
- Starvation: long processes may never run if short ones keep arriving β fix with aging.
- Burst time is unknown in practice β predicted via exponential averaging of past behaviour.
Interview Questions on This Topic
- QProve that SJF minimises average waiting time.
- QWhat is the difference between SJF and SRTF?
- QHow does aging solve the starvation problem in SJF?
- QCalculate average waiting time for SRTF: P1(AT=0,BT=8), P2(AT=1,BT=4), P3(AT=2,BT=2).
Frequently Asked Questions
Is SJF or SRTF used in real operating systems?
Not directly β real OSes don't know burst times. However, Linux's Completely Fair Scheduler (CFS) approximates SJF behaviour by tracking process runtime and favouring processes that have used less CPU time recently.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.