From f806331e3cd104f3ff2532c7a31ec24c54707a14 Mon Sep 17 00:00:00 2001 From: DongJoon Cha Date: Tue, 24 May 2022 13:31:42 +0900 Subject: [PATCH 1/7] Add Multi-Level-Feedback-Queue scheduling algorithm --- scheduling/multi_level_feedback_queue.py | 317 +++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 scheduling/multi_level_feedback_queue.py diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py new file mode 100644 index 000000000000..9c0f66040999 --- /dev/null +++ b/scheduling/multi_level_feedback_queue.py @@ -0,0 +1,317 @@ +from collections import deque + + +class Process: + """ + Process should have these fields below + """ + + 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 + This MLFQ has a lot of queue that has different priority + In this MLFQ, + The first Queue(0) to last second Queue(N-2) of MLFQ has Round Robin Algorithm + The last Queue(N-1) has First Come, First Served Algorithm + """ + + def __init__( + self, + number_of_queues: int, + time_slices: int, + queue: list[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() + + 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: list[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] + """ + remaining_burst_times = [] + for i in range(len(queue)): + remaining_burst_times.append(queue[i].burst_time) + return remaining_burst_times + + 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: list[Process]) -> list[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() # 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: list[Process], time_slice: int) -> list[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() # 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) -> list[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()}" + ) From 3af252793fa5f310426bb6b9b94818f6b1f321a3 Mon Sep 17 00:00:00 2001 From: DongJoon Cha Date: Wed, 25 May 2022 17:00:46 +0900 Subject: [PATCH 2/7] fix type hint annotation for pre-commit --- scheduling/multi_level_feedback_queue.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index 9c0f66040999..57f127830304 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -29,8 +29,8 @@ class MLFQ: def __init__( self, number_of_queues: int, - time_slices: int, - queue: list[Process], + time_slices: list[int], + queue: deque[Process], current_time: int, ) -> None: # total number of mlfq's queues @@ -42,7 +42,7 @@ def __init__( # current time self.current_time = current_time # finished process is in this sequence queue - self.finish_queue = deque() + self.finish_queue: deque[Process] = deque() def calculate_sequence_of_finish_queue(self) -> list[str]: """ @@ -113,7 +113,7 @@ def calculate_completion_time(self, queue: list[Process]) -> list[int]: return completion_times def calculate_remaining_burst_time_of_processes( - self, queue: list[Process] + self, queue: deque[Process] ) -> list[int]: """ This method calculate remaining burst time of processes @@ -154,7 +154,7 @@ def update_waiting_time(self, process: Process) -> int: process.waiting_time += self.current_time - process.stop_time return process.waiting_time - def first_come_first_served(self, ready_queue: list[Process]) -> list[Process]: + 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 @@ -168,7 +168,7 @@ def first_come_first_served(self, ready_queue: list[Process]) -> list[Process]: >>> mlfq.calculate_sequence_of_finish_queue() ['P1', 'P2', 'P3', 'P4'] """ - finished = deque() # sequence deque of finished process + finished: deque[Process] = deque() # sequence deque of finished process while len(ready_queue) != 0: cp = ready_queue.popleft() # current process @@ -193,7 +193,9 @@ def first_come_first_served(self, ready_queue: list[Process]) -> list[Process]: # FCFS will finish all remaining processes return finished - def round_robin(self, ready_queue: list[Process], time_slice: int) -> list[Process]: + 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 @@ -208,7 +210,7 @@ def round_robin(self, ready_queue: list[Process], time_slice: int) -> list[Proce >>> mlfq.calculate_sequence_of_finish_queue() ['P2'] """ - finished = deque() # sequence deque of terminated process + 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 @@ -245,7 +247,7 @@ def round_robin(self, ready_queue: list[Process], time_slice: int) -> list[Proce # return finished processes queue and remaining processes queue return finished, ready_queue - def multi_level_feedback_queue(self) -> list[Process]: + def multi_level_feedback_queue(self) -> deque[Process]: """ MLFQ(Multi Level Feedback Queue) >>> P1 = Process("P1", 0, 53) From 2e8d989f3782e87e104ed16f1c4c22ec99a6f99e Mon Sep 17 00:00:00 2001 From: DongJoon Cha <81581204+dongjji@users.noreply.github.com> Date: Thu, 2 Jun 2022 13:40:22 +0900 Subject: [PATCH 3/7] Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law --- scheduling/multi_level_feedback_queue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index 57f127830304..94d909c24041 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -20,7 +20,7 @@ class MLFQ: """ MLFQ(Multi Level Feedback Queue) https://en.wikipedia.org/wiki/Multilevel_feedback_queue - This MLFQ has a lot of queue that has different priority + 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 has Round Robin Algorithm The last Queue(N-1) has First Come, First Served Algorithm From 0a9b3b3048a11ba0751ebe1675d36b3a4c7993d7 Mon Sep 17 00:00:00 2001 From: DongJoon Cha <81581204+dongjji@users.noreply.github.com> Date: Thu, 2 Jun 2022 13:40:29 +0900 Subject: [PATCH 4/7] Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law --- scheduling/multi_level_feedback_queue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index 94d909c24041..abd25af7c051 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -22,7 +22,7 @@ class MLFQ: 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 has Round Robin Algorithm + 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 """ From a1117d8523dc2d2e3fbb6186ee43a1a08b0314d3 Mon Sep 17 00:00:00 2001 From: DongJoon Cha <81581204+dongjji@users.noreply.github.com> Date: Thu, 2 Jun 2022 13:40:34 +0900 Subject: [PATCH 5/7] Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law --- scheduling/multi_level_feedback_queue.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index abd25af7c051..e8a25ca13738 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -133,10 +133,7 @@ def calculate_remaining_burst_time_of_processes( >>> mlfq.calculate_remaining_burst_time_of_processes(ready_queue) [11, 26] """ - remaining_burst_times = [] - for i in range(len(queue)): - remaining_burst_times.append(queue[i].burst_time) - return remaining_burst_times + return [q.burst_time for q in queue] def update_waiting_time(self, process: Process) -> int: """ From 84643fc3f590c45d706ccabb020fa3d980938852 Mon Sep 17 00:00:00 2001 From: DongJoon Cha Date: Thu, 2 Jun 2022 13:42:24 +0900 Subject: [PATCH 6/7] Update scheduling/multi_level_feedback_queue.py --- scheduling/multi_level_feedback_queue.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index e8a25ca13738..f08e5c91be24 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -2,9 +2,6 @@ class Process: - """ - Process should have these fields below - """ def __init__(self, process_name: str, arrival_time: int, burst_time: int) -> None: self.process_name = process_name # process name From 210e395a383e1276bcf50003e4a7a96988d1f536 Mon Sep 17 00:00:00 2001 From: DongJoon Cha <81581204+dongjji@users.noreply.github.com> Date: Sat, 4 Jun 2022 20:30:37 +0900 Subject: [PATCH 7/7] Update scheduling/multi_level_feedback_queue.py Co-authored-by: John Law --- scheduling/multi_level_feedback_queue.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index f08e5c91be24..95ca827e062d 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -2,7 +2,6 @@ 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