mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-12-18 01:00:15 +00:00
a44afc9b7d
* 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>
313 lines
12 KiB
Python
313 lines
12 KiB
Python
from collections import deque
|
|
|
|
|
|
class Process:
|
|
def __init__(self, process_name: str, arrival_time: int, burst_time: int) -> None:
|
|
self.process_name = process_name # process name
|
|
self.arrival_time = arrival_time # arrival time of the process
|
|
# completion time of finished process or last interrupted time
|
|
self.stop_time = arrival_time
|
|
self.burst_time = burst_time # remaining burst time
|
|
self.waiting_time = 0 # total time of the process wait in ready queue
|
|
self.turnaround_time = 0 # time from arrival time to completion time
|
|
|
|
|
|
class MLFQ:
|
|
"""
|
|
MLFQ(Multi Level Feedback Queue)
|
|
https://en.wikipedia.org/wiki/Multilevel_feedback_queue
|
|
MLFQ has a lot of queues that have different priority
|
|
In this MLFQ,
|
|
The first Queue(0) to last second Queue(N-2) of MLFQ have Round Robin Algorithm
|
|
The last Queue(N-1) has First Come, First Served Algorithm
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
number_of_queues: int,
|
|
time_slices: list[int],
|
|
queue: deque[Process],
|
|
current_time: int,
|
|
) -> None:
|
|
# total number of mlfq's queues
|
|
self.number_of_queues = number_of_queues
|
|
# time slice of queues that round robin algorithm applied
|
|
self.time_slices = time_slices
|
|
# unfinished process is in this ready_queue
|
|
self.ready_queue = queue
|
|
# current time
|
|
self.current_time = current_time
|
|
# finished process is in this sequence queue
|
|
self.finish_queue: deque[Process] = deque()
|
|
|
|
def calculate_sequence_of_finish_queue(self) -> list[str]:
|
|
"""
|
|
This method returns the sequence of finished processes
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> _ = mlfq.multi_level_feedback_queue()
|
|
>>> mlfq.calculate_sequence_of_finish_queue()
|
|
['P2', 'P4', 'P1', 'P3']
|
|
"""
|
|
sequence = []
|
|
for i in range(len(self.finish_queue)):
|
|
sequence.append(self.finish_queue[i].process_name)
|
|
return sequence
|
|
|
|
def calculate_waiting_time(self, queue: list[Process]) -> list[int]:
|
|
"""
|
|
This method calculates waiting time of processes
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> _ = mlfq.multi_level_feedback_queue()
|
|
>>> mlfq.calculate_waiting_time([P1, P2, P3, P4])
|
|
[83, 17, 94, 101]
|
|
"""
|
|
waiting_times = []
|
|
for i in range(len(queue)):
|
|
waiting_times.append(queue[i].waiting_time)
|
|
return waiting_times
|
|
|
|
def calculate_turnaround_time(self, queue: list[Process]) -> list[int]:
|
|
"""
|
|
This method calculates turnaround time of processes
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> _ = mlfq.multi_level_feedback_queue()
|
|
>>> mlfq.calculate_turnaround_time([P1, P2, P3, P4])
|
|
[136, 34, 162, 125]
|
|
"""
|
|
turnaround_times = []
|
|
for i in range(len(queue)):
|
|
turnaround_times.append(queue[i].turnaround_time)
|
|
return turnaround_times
|
|
|
|
def calculate_completion_time(self, queue: list[Process]) -> list[int]:
|
|
"""
|
|
This method calculates completion time of processes
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> _ = mlfq.multi_level_feedback_queue()
|
|
>>> mlfq.calculate_turnaround_time([P1, P2, P3, P4])
|
|
[136, 34, 162, 125]
|
|
"""
|
|
completion_times = []
|
|
for i in range(len(queue)):
|
|
completion_times.append(queue[i].stop_time)
|
|
return completion_times
|
|
|
|
def calculate_remaining_burst_time_of_processes(
|
|
self, queue: deque[Process]
|
|
) -> list[int]:
|
|
"""
|
|
This method calculate remaining burst time of processes
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> finish_queue, ready_queue = mlfq.round_robin(deque([P1, P2, P3, P4]), 17)
|
|
>>> mlfq.calculate_remaining_burst_time_of_processes(mlfq.finish_queue)
|
|
[0]
|
|
>>> mlfq.calculate_remaining_burst_time_of_processes(ready_queue)
|
|
[36, 51, 7]
|
|
>>> finish_queue, ready_queue = mlfq.round_robin(ready_queue, 25)
|
|
>>> mlfq.calculate_remaining_burst_time_of_processes(mlfq.finish_queue)
|
|
[0, 0]
|
|
>>> mlfq.calculate_remaining_burst_time_of_processes(ready_queue)
|
|
[11, 26]
|
|
"""
|
|
return [q.burst_time for q in queue]
|
|
|
|
def update_waiting_time(self, process: Process) -> int:
|
|
"""
|
|
This method updates waiting times of unfinished processes
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> mlfq.current_time = 10
|
|
>>> P1.stop_time = 5
|
|
>>> mlfq.update_waiting_time(P1)
|
|
5
|
|
"""
|
|
process.waiting_time += self.current_time - process.stop_time
|
|
return process.waiting_time
|
|
|
|
def first_come_first_served(self, ready_queue: deque[Process]) -> deque[Process]:
|
|
"""
|
|
FCFS(First Come, First Served)
|
|
FCFS will be applied to MLFQ's last queue
|
|
A first came process will be finished at first
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> _ = mlfq.first_come_first_served(mlfq.ready_queue)
|
|
>>> mlfq.calculate_sequence_of_finish_queue()
|
|
['P1', 'P2', 'P3', 'P4']
|
|
"""
|
|
finished: deque[Process] = deque() # sequence deque of finished process
|
|
while len(ready_queue) != 0:
|
|
cp = ready_queue.popleft() # current process
|
|
|
|
# if process's arrival time is later than current time, update current time
|
|
if self.current_time < cp.arrival_time:
|
|
self.current_time += cp.arrival_time
|
|
|
|
# update waiting time of current process
|
|
self.update_waiting_time(cp)
|
|
# update current time
|
|
self.current_time += cp.burst_time
|
|
# finish the process and set the process's burst-time 0
|
|
cp.burst_time = 0
|
|
# set the process's turnaround time because it is finished
|
|
cp.turnaround_time = self.current_time - cp.arrival_time
|
|
# set the completion time
|
|
cp.stop_time = self.current_time
|
|
# add the process to queue that has finished queue
|
|
finished.append(cp)
|
|
|
|
self.finish_queue.extend(finished) # add finished process to finish queue
|
|
# FCFS will finish all remaining processes
|
|
return finished
|
|
|
|
def round_robin(
|
|
self, ready_queue: deque[Process], time_slice: int
|
|
) -> tuple[deque[Process], deque[Process]]:
|
|
"""
|
|
RR(Round Robin)
|
|
RR will be applied to MLFQ's all queues except last queue
|
|
All processes can't use CPU for time more than time_slice
|
|
If the process consume CPU up to time_slice, it will go back to ready queue
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> finish_queue, ready_queue = mlfq.round_robin(mlfq.ready_queue, 17)
|
|
>>> mlfq.calculate_sequence_of_finish_queue()
|
|
['P2']
|
|
"""
|
|
finished: deque[Process] = deque() # sequence deque of terminated process
|
|
# just for 1 cycle and unfinished processes will go back to queue
|
|
for i in range(len(ready_queue)):
|
|
cp = ready_queue.popleft() # current process
|
|
|
|
# if process's arrival time is later than current time, update current time
|
|
if self.current_time < cp.arrival_time:
|
|
self.current_time += cp.arrival_time
|
|
|
|
# update waiting time of unfinished processes
|
|
self.update_waiting_time(cp)
|
|
# if the burst time of process is bigger than time-slice
|
|
if cp.burst_time > time_slice:
|
|
# use CPU for only time-slice
|
|
self.current_time += time_slice
|
|
# update remaining burst time
|
|
cp.burst_time -= time_slice
|
|
# update end point time
|
|
cp.stop_time = self.current_time
|
|
# locate the process behind the queue because it is not finished
|
|
ready_queue.append(cp)
|
|
else:
|
|
# use CPU for remaining burst time
|
|
self.current_time += cp.burst_time
|
|
# set burst time 0 because the process is finished
|
|
cp.burst_time = 0
|
|
# set the finish time
|
|
cp.stop_time = self.current_time
|
|
# update the process' turnaround time because it is finished
|
|
cp.turnaround_time = self.current_time - cp.arrival_time
|
|
# add the process to queue that has finished queue
|
|
finished.append(cp)
|
|
|
|
self.finish_queue.extend(finished) # add finished process to finish queue
|
|
# return finished processes queue and remaining processes queue
|
|
return finished, ready_queue
|
|
|
|
def multi_level_feedback_queue(self) -> deque[Process]:
|
|
"""
|
|
MLFQ(Multi Level Feedback Queue)
|
|
>>> P1 = Process("P1", 0, 53)
|
|
>>> P2 = Process("P2", 0, 17)
|
|
>>> P3 = Process("P3", 0, 68)
|
|
>>> P4 = Process("P4", 0, 24)
|
|
>>> mlfq = MLFQ(3, [17, 25], deque([P1, P2, P3, P4]), 0)
|
|
>>> finish_queue = mlfq.multi_level_feedback_queue()
|
|
>>> mlfq.calculate_sequence_of_finish_queue()
|
|
['P2', 'P4', 'P1', 'P3']
|
|
"""
|
|
|
|
# all queues except last one have round_robin algorithm
|
|
for i in range(self.number_of_queues - 1):
|
|
finished, self.ready_queue = self.round_robin(
|
|
self.ready_queue, self.time_slices[i]
|
|
)
|
|
# the last queue has first_come_first_served algorithm
|
|
self.first_come_first_served(self.ready_queue)
|
|
|
|
return self.finish_queue
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
|
|
P1 = Process("P1", 0, 53)
|
|
P2 = Process("P2", 0, 17)
|
|
P3 = Process("P3", 0, 68)
|
|
P4 = Process("P4", 0, 24)
|
|
number_of_queues = 3
|
|
time_slices = [17, 25]
|
|
queue = deque([P1, P2, P3, P4])
|
|
|
|
if len(time_slices) != number_of_queues - 1:
|
|
exit()
|
|
|
|
doctest.testmod(extraglobs={"queue": deque([P1, P2, P3, P4])})
|
|
|
|
P1 = Process("P1", 0, 53)
|
|
P2 = Process("P2", 0, 17)
|
|
P3 = Process("P3", 0, 68)
|
|
P4 = Process("P4", 0, 24)
|
|
number_of_queues = 3
|
|
time_slices = [17, 25]
|
|
queue = deque([P1, P2, P3, P4])
|
|
mlfq = MLFQ(number_of_queues, time_slices, queue, 0)
|
|
finish_queue = mlfq.multi_level_feedback_queue()
|
|
|
|
# print total waiting times of processes(P1, P2, P3, P4)
|
|
print(
|
|
f"waiting time:\
|
|
\t\t\t{MLFQ.calculate_waiting_time(mlfq, [P1, P2, P3, P4])}"
|
|
)
|
|
# print completion times of processes(P1, P2, P3, P4)
|
|
print(
|
|
f"completion time:\
|
|
\t\t{MLFQ.calculate_completion_time(mlfq, [P1, P2, P3, P4])}"
|
|
)
|
|
# print total turnaround times of processes(P1, P2, P3, P4)
|
|
print(
|
|
f"turnaround time:\
|
|
\t\t{MLFQ.calculate_turnaround_time(mlfq, [P1, P2, P3, P4])}"
|
|
)
|
|
# print sequence of finished processes
|
|
print(
|
|
f"sequnece of finished processes:\
|
|
{mlfq.calculate_sequence_of_finish_queue()}"
|
|
)
|