Skip to content

Commit a44afc9

Browse files
devychapoyea
andauthored
Add Multi-Level-Feedback-Queue scheduling algorithm (TheAlgorithms#6165)
* Add Multi-Level-Feedback-Queue scheduling algorithm * fix type hint annotation for pre-commit * Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law <johnlaw.po@gmail.com> * Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law <johnlaw.po@gmail.com> * Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law <johnlaw.po@gmail.com> * Update scheduling/multi_level_feedback_queue.py * Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law <johnlaw.po@gmail.com> Co-authored-by: John Law <johnlaw.po@gmail.com>
1 parent 8004671 commit a44afc9

File tree

1 file changed

+312
-0
lines changed

1 file changed

+312
-0
lines changed
+312
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
from collections import deque
2+
3+
4+
class Process:
5+
def __init__(self, process_name: str, arrival_time: int, burst_time: int) -> None:
6+
self.process_name = process_name # process name
7+
self.arrival_time = arrival_time # arrival time of the process
8+
# completion time of finished process or last interrupted time
9+
self.stop_time = arrival_time
10+
self.burst_time = burst_time # remaining burst time
11+
self.waiting_time = 0 # total time of the process wait in ready queue
12+
self.turnaround_time = 0 # time from arrival time to completion time
13+
14+
15+
class MLFQ:
16+
"""
17+
MLFQ(Multi Level Feedback Queue)
18+
https://en.wikipedia.org/wiki/Multilevel_feedback_queue
19+
MLFQ has a lot of queues that have different priority
20+
In this MLFQ,
21+
The first Queue(0) to last second Queue(N-2) of MLFQ have Round Robin Algorithm
22+
The last Queue(N-1) has First Come, First Served Algorithm
23+
"""
24+
25+
def __init__(
26+
self,
27+
number_of_queues: int,
28+
time_slices: list[int],
29+
queue: deque[Process],
30+
current_time: int,
31+
) -> None:
32+
# total number of mlfq's queues
33+
self.number_of_queues = number_of_queues
34+
# time slice of queues that round robin algorithm applied
35+
self.time_slices = time_slices
36+
# unfinished process is in this ready_queue
37+
self.ready_queue = queue
38+
# current time
39+
self.current_time = current_time
40+
# finished process is in this sequence queue
41+
self.finish_queue: deque[Process] = deque()
42+
43+
def calculate_sequence_of_finish_queue(self) -> list[str]:
44+
"""
45+
This method returns the sequence of finished processes
46+
>>> P1 = Process("P1", 0, 53)
47+
>>> P2 = Process("P2", 0, 17)
48+
>>> P3 = Process("P3", 0, 68)
49+
>>> P4 = Process("P4", 0, 24)
50+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
51+
>>> _ = mlfq.multi_level_feedback_queue()
52+
>>> mlfq.calculate_sequence_of_finish_queue()
53+
['P2', 'P4', 'P1', 'P3']
54+
"""
55+
sequence = []
56+
for i in range(len(self.finish_queue)):
57+
sequence.append(self.finish_queue[i].process_name)
58+
return sequence
59+
60+
def calculate_waiting_time(self, queue: list[Process]) -> list[int]:
61+
"""
62+
This method calculates waiting time of processes
63+
>>> P1 = Process("P1", 0, 53)
64+
>>> P2 = Process("P2", 0, 17)
65+
>>> P3 = Process("P3", 0, 68)
66+
>>> P4 = Process("P4", 0, 24)
67+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
68+
>>> _ = mlfq.multi_level_feedback_queue()
69+
>>> mlfq.calculate_waiting_time([P1, P2, P3, P4])
70+
[83, 17, 94, 101]
71+
"""
72+
waiting_times = []
73+
for i in range(len(queue)):
74+
waiting_times.append(queue[i].waiting_time)
75+
return waiting_times
76+
77+
def calculate_turnaround_time(self, queue: list[Process]) -> list[int]:
78+
"""
79+
This method calculates turnaround time of processes
80+
>>> P1 = Process("P1", 0, 53)
81+
>>> P2 = Process("P2", 0, 17)
82+
>>> P3 = Process("P3", 0, 68)
83+
>>> P4 = Process("P4", 0, 24)
84+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
85+
>>> _ = mlfq.multi_level_feedback_queue()
86+
>>> mlfq.calculate_turnaround_time([P1, P2, P3, P4])
87+
[136, 34, 162, 125]
88+
"""
89+
turnaround_times = []
90+
for i in range(len(queue)):
91+
turnaround_times.append(queue[i].turnaround_time)
92+
return turnaround_times
93+
94+
def calculate_completion_time(self, queue: list[Process]) -> list[int]:
95+
"""
96+
This method calculates completion time of processes
97+
>>> P1 = Process("P1", 0, 53)
98+
>>> P2 = Process("P2", 0, 17)
99+
>>> P3 = Process("P3", 0, 68)
100+
>>> P4 = Process("P4", 0, 24)
101+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
102+
>>> _ = mlfq.multi_level_feedback_queue()
103+
>>> mlfq.calculate_turnaround_time([P1, P2, P3, P4])
104+
[136, 34, 162, 125]
105+
"""
106+
completion_times = []
107+
for i in range(len(queue)):
108+
completion_times.append(queue[i].stop_time)
109+
return completion_times
110+
111+
def calculate_remaining_burst_time_of_processes(
112+
self, queue: deque[Process]
113+
) -> list[int]:
114+
"""
115+
This method calculate remaining burst time of processes
116+
>>> P1 = Process("P1", 0, 53)
117+
>>> P2 = Process("P2", 0, 17)
118+
>>> P3 = Process("P3", 0, 68)
119+
>>> P4 = Process("P4", 0, 24)
120+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
121+
>>> finish_queue, ready_queue = mlfq.round_robin(deque([P1, P2, P3, P4]), 17)
122+
>>> mlfq.calculate_remaining_burst_time_of_processes(mlfq.finish_queue)
123+
[0]
124+
>>> mlfq.calculate_remaining_burst_time_of_processes(ready_queue)
125+
[36, 51, 7]
126+
>>> finish_queue, ready_queue = mlfq.round_robin(ready_queue, 25)
127+
>>> mlfq.calculate_remaining_burst_time_of_processes(mlfq.finish_queue)
128+
[0, 0]
129+
>>> mlfq.calculate_remaining_burst_time_of_processes(ready_queue)
130+
[11, 26]
131+
"""
132+
return [q.burst_time for q in queue]
133+
134+
def update_waiting_time(self, process: Process) -> int:
135+
"""
136+
This method updates waiting times of unfinished processes
137+
>>> P1 = Process("P1", 0, 53)
138+
>>> P2 = Process("P2", 0, 17)
139+
>>> P3 = Process("P3", 0, 68)
140+
>>> P4 = Process("P4", 0, 24)
141+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
142+
>>> mlfq.current_time = 10
143+
>>> P1.stop_time = 5
144+
>>> mlfq.update_waiting_time(P1)
145+
5
146+
"""
147+
process.waiting_time += self.current_time - process.stop_time
148+
return process.waiting_time
149+
150+
def first_come_first_served(self, ready_queue: deque[Process]) -> deque[Process]:
151+
"""
152+
FCFS(First Come, First Served)
153+
FCFS will be applied to MLFQ's last queue
154+
A first came process will be finished at first
155+
>>> P1 = Process("P1", 0, 53)
156+
>>> P2 = Process("P2", 0, 17)
157+
>>> P3 = Process("P3", 0, 68)
158+
>>> P4 = Process("P4", 0, 24)
159+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
160+
>>> _ = mlfq.first_come_first_served(mlfq.ready_queue)
161+
>>> mlfq.calculate_sequence_of_finish_queue()
162+
['P1', 'P2', 'P3', 'P4']
163+
"""
164+
finished: deque[Process] = deque() # sequence deque of finished process
165+
while len(ready_queue) != 0:
166+
cp = ready_queue.popleft() # current process
167+
168+
# if process's arrival time is later than current time, update current time
169+
if self.current_time < cp.arrival_time:
170+
self.current_time += cp.arrival_time
171+
172+
# update waiting time of current process
173+
self.update_waiting_time(cp)
174+
# update current time
175+
self.current_time += cp.burst_time
176+
# finish the process and set the process's burst-time 0
177+
cp.burst_time = 0
178+
# set the process's turnaround time because it is finished
179+
cp.turnaround_time = self.current_time - cp.arrival_time
180+
# set the completion time
181+
cp.stop_time = self.current_time
182+
# add the process to queue that has finished queue
183+
finished.append(cp)
184+
185+
self.finish_queue.extend(finished) # add finished process to finish queue
186+
# FCFS will finish all remaining processes
187+
return finished
188+
189+
def round_robin(
190+
self, ready_queue: deque[Process], time_slice: int
191+
) -> tuple[deque[Process], deque[Process]]:
192+
"""
193+
RR(Round Robin)
194+
RR will be applied to MLFQ's all queues except last queue
195+
All processes can't use CPU for time more than time_slice
196+
If the process consume CPU up to time_slice, it will go back to ready queue
197+
>>> P1 = Process("P1", 0, 53)
198+
>>> P2 = Process("P2", 0, 17)
199+
>>> P3 = Process("P3", 0, 68)
200+
>>> P4 = Process("P4", 0, 24)
201+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
202+
>>> finish_queue, ready_queue = mlfq.round_robin(mlfq.ready_queue, 17)
203+
>>> mlfq.calculate_sequence_of_finish_queue()
204+
['P2']
205+
"""
206+
finished: deque[Process] = deque() # sequence deque of terminated process
207+
# just for 1 cycle and unfinished processes will go back to queue
208+
for i in range(len(ready_queue)):
209+
cp = ready_queue.popleft() # current process
210+
211+
# if process's arrival time is later than current time, update current time
212+
if self.current_time < cp.arrival_time:
213+
self.current_time += cp.arrival_time
214+
215+
# update waiting time of unfinished processes
216+
self.update_waiting_time(cp)
217+
# if the burst time of process is bigger than time-slice
218+
if cp.burst_time > time_slice:
219+
# use CPU for only time-slice
220+
self.current_time += time_slice
221+
# update remaining burst time
222+
cp.burst_time -= time_slice
223+
# update end point time
224+
cp.stop_time = self.current_time
225+
# locate the process behind the queue because it is not finished
226+
ready_queue.append(cp)
227+
else:
228+
# use CPU for remaining burst time
229+
self.current_time += cp.burst_time
230+
# set burst time 0 because the process is finished
231+
cp.burst_time = 0
232+
# set the finish time
233+
cp.stop_time = self.current_time
234+
# update the process' turnaround time because it is finished
235+
cp.turnaround_time = self.current_time - cp.arrival_time
236+
# add the process to queue that has finished queue
237+
finished.append(cp)
238+
239+
self.finish_queue.extend(finished) # add finished process to finish queue
240+
# return finished processes queue and remaining processes queue
241+
return finished, ready_queue
242+
243+
def multi_level_feedback_queue(self) -> deque[Process]:
244+
"""
245+
MLFQ(Multi Level Feedback Queue)
246+
>>> P1 = Process("P1", 0, 53)
247+
>>> P2 = Process("P2", 0, 17)
248+
>>> P3 = Process("P3", 0, 68)
249+
>>> P4 = Process("P4", 0, 24)
250+
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
251+
>>> finish_queue = mlfq.multi_level_feedback_queue()
252+
>>> mlfq.calculate_sequence_of_finish_queue()
253+
['P2', 'P4', 'P1', 'P3']
254+
"""
255+
256+
# all queues except last one have round_robin algorithm
257+
for i in range(self.number_of_queues - 1):
258+
finished, self.ready_queue = self.round_robin(
259+
self.ready_queue, self.time_slices[i]
260+
)
261+
# the last queue has first_come_first_served algorithm
262+
self.first_come_first_served(self.ready_queue)
263+
264+
return self.finish_queue
265+
266+
267+
if __name__ == "__main__":
268+
import doctest
269+
270+
P1 = Process("P1", 0, 53)
271+
P2 = Process("P2", 0, 17)
272+
P3 = Process("P3", 0, 68)
273+
P4 = Process("P4", 0, 24)
274+
number_of_queues = 3
275+
time_slices = [17, 25]
276+
queue = deque([P1, P2, P3, P4])
277+
278+
if len(time_slices) != number_of_queues - 1:
279+
exit()
280+
281+
doctest.testmod(extraglobs={"queue": deque([P1, P2, P3, P4])})
282+
283+
P1 = Process("P1", 0, 53)
284+
P2 = Process("P2", 0, 17)
285+
P3 = Process("P3", 0, 68)
286+
P4 = Process("P4", 0, 24)
287+
number_of_queues = 3
288+
time_slices = [17, 25]
289+
queue = deque([P1, P2, P3, P4])
290+
mlfq = MLFQ(number_of_queues, time_slices, queue, 0)
291+
finish_queue = mlfq.multi_level_feedback_queue()
292+
293+
# print total waiting times of processes(P1, P2, P3, P4)
294+
print(
295+
f"waiting time:\
296+
\t\t\t{MLFQ.calculate_waiting_time(mlfq, [P1, P2, P3, P4])}"
297+
)
298+
# print completion times of processes(P1, P2, P3, P4)
299+
print(
300+
f"completion time:\
301+
\t\t{MLFQ.calculate_completion_time(mlfq, [P1, P2, P3, P4])}"
302+
)
303+
# print total turnaround times of processes(P1, P2, P3, P4)
304+
print(
305+
f"turnaround time:\
306+
\t\t{MLFQ.calculate_turnaround_time(mlfq, [P1, P2, P3, P4])}"
307+
)
308+
# print sequence of finished processes
309+
print(
310+
f"sequnece of finished processes:\
311+
{mlfq.calculate_sequence_of_finish_queue()}"
312+
)

0 commit comments

Comments
 (0)