Skip to content

Commit 9d9b51f

Browse files
Create non_preemptive_sjf_scheduling.cpp
I have done the necessary changes. The test function will generate 10 different testcases in which it will print the before and after the SJF scheduling. @realstealthninja Kindly review the PR and please accept it.
1 parent 7af8d8a commit 9d9b51f

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/**
2+
* @file
3+
* @brief Implementation of SJF CPU scheduling algorithm
4+
* @details
5+
* shortest job first (SJF), also known as shortest job next (SJN), is a scheduling policy
6+
* that selects for execution the waiting process with the smallest execution time.
7+
* SJN is a non-preemptive algorithm. Shortest remaining time is a preemptive variant of SJN.
8+
* @link https://www.guru99.com/shortest-job-first-sjf-scheduling.html
9+
* @author [Lakshmi Srikumar](https://github.com/LakshmiSrikumar)
10+
*/
11+
12+
#include <algorithm> /// for sorting
13+
#include <cassert> /// for assert
14+
#include <random> /// random number generation
15+
#include <iomanip> /// for formatting the output
16+
#include <iostream> /// for IO operations
17+
#include <queue> /// for std::priority_queue
18+
#include <unordered_set> /// for std::unordered_set
19+
#include <vector> /// for std::vector
20+
21+
using std::cin;
22+
using std::cout;
23+
using std::endl;
24+
using std::get;
25+
using std::left;
26+
using std::make_tuple;
27+
using std::priority_queue;
28+
using std::tuple;
29+
using std::unordered_set;
30+
using std::vector;
31+
32+
/**
33+
* @brief Comparator function for sorting a vector
34+
* @tparam S Data type of Process ID
35+
* @tparam T Data type of Arrival time
36+
* @tparam E Data type of Burst time
37+
* @param t1 First tuple<S,T,E>t1
38+
* @param t2 Second tuple<S,T,E>t2
39+
* @returns true if t1 and t2 are in the CORRECT order
40+
* @returns false if t1 and t2 are in the INCORRECT order
41+
*/
42+
template <typename S, typename T, typename E>
43+
bool sortcol(tuple<S, T, E>& t1, tuple<S, T, E>& t2) {
44+
if (get<1>(t1) < get<1>(t2)) {
45+
return true;
46+
} else if (get<1>(t1) == get<1>(t2) && get<0>(t1) < get<0>(t2)) {
47+
return true;
48+
}
49+
return false;
50+
}
51+
52+
/**
53+
* @class Compare
54+
* @brief Comparator class for priority queue
55+
* @tparam S Data type of Process ID
56+
* @tparam T Data type of Arrival time
57+
* @tparam E Data type of Burst time
58+
*/
59+
template <typename S, typename T, typename E>
60+
class Compare {
61+
public:
62+
63+
/**
64+
* @param t1 First tuple
65+
* @param t2 Second tuple
66+
* @brief A comparator function that checks whether to swap the two tuples
67+
* or not.
68+
* @link Refer to
69+
* https://www.geeksforgeeks.org/comparator-class-in-c-with-examples/ for
70+
* detailed description of comparator
71+
* @returns true if the tuples SHOULD be swapped
72+
* @returns false if the tuples SHOULDN'T be swapped
73+
*/
74+
bool operator()(tuple<S, T, E, double, double, double>& t1,
75+
tuple<S, T, E, double, double, double>& t2) {
76+
// Compare burst times for SJF
77+
if (get<2>(t2) < get<2>(t1)) {
78+
return true;
79+
}
80+
// If burst times are the same, compare arrival times
81+
else if (get<2>(t2) == get<2>(t1)) {
82+
return get<1>(t2) < get<1>(t1);
83+
}
84+
return false;
85+
}
86+
};
87+
88+
/**
89+
* @class SJF
90+
* @brief Class which implements the SJF scheduling algorithm
91+
* @tparam S Data type of Process ID
92+
* @tparam T Data type of Arrival time
93+
* @tparam E Data type of Burst time
94+
*/
95+
template <typename S, typename T, typename E>
96+
class SJF {
97+
/**
98+
* Priority queue of schedules(stored as tuples) of processes.
99+
* In each tuple
100+
* 1st element: Process ID
101+
* 2nd element: Arrival Time
102+
* 3rd element: Burst time
103+
* 4th element: Completion time
104+
* 5th element: Turnaround time
105+
* 6th element: Waiting time
106+
*/
107+
priority_queue<tuple<S, T, E, double, double, double>,
108+
vector<tuple<S, T, E, double, double, double>>,
109+
Compare<S, T, E>> schedule;
110+
111+
// Stores final status of all the processes after completing the execution.
112+
vector<tuple<S, T, E, double, double, double>> result;
113+
114+
// Stores process IDs. Used for confirming absence of a process while it.
115+
unordered_set<S> idList;
116+
117+
public:
118+
/**
119+
* @brief Adds the process to the ready queue if it isn't already there
120+
* @param id Process ID
121+
* @param arrival Arrival time of the process
122+
* @param burst Burst time of the process
123+
* @returns void
124+
*
125+
*/
126+
void addProcess(S id, T arrival, E burst) {
127+
// Add if a process with process ID as id is not found in idList.
128+
if (idList.find(id) == idList.end()) {
129+
tuple<S, T, E, double, double, double> t =
130+
make_tuple(id, arrival, burst, 0, 0, 0);
131+
schedule.push(t);
132+
idList.insert(id);
133+
}
134+
}
135+
136+
/**
137+
* @brief Algorithm for scheduling CPU processes according to the Shortest Job
138+
First (SJF) scheduling algorithm.
139+
*
140+
* @details Non pre-emptive SJF is an algorithm that schedules processes based on the length
141+
* of their burst times. The process with the smallest burst time is executed first.
142+
* In a non-preemptive scheduling algorithm, once a process starts executing,
143+
* it runs to completion without being interrupted.
144+
*
145+
* I used a min priority queue because it allows you to efficiently pick the process
146+
* with the smallest burst time in constant time, by maintaining a priority order where
147+
* the shortest burst process is always at the front.
148+
*
149+
* @returns void
150+
*/
151+
152+
vector<tuple<S, T, E, double, double, double>> scheduleForSJF() {
153+
// Variable to keep track of time elapsed so far
154+
double timeElapsed = 0;
155+
156+
while (!schedule.empty()) {
157+
tuple<S, T, E, double, double, double> cur = schedule.top();
158+
159+
// If the current process arrived at time t2, the last process
160+
// completed its execution at time t1, and t2 > t1.
161+
if (get<1>(cur) > timeElapsed) {
162+
timeElapsed += get<1>(cur) - timeElapsed;
163+
}
164+
165+
// Add Burst time to time elapsed
166+
timeElapsed += get<2>(cur);
167+
168+
// Completion time of the current process will be same as time
169+
// elapsed so far
170+
get<3>(cur) = timeElapsed;
171+
172+
// Turnaround time = Completion time - Arrival time
173+
get<4>(cur) = get<3>(cur) - get<1>(cur);
174+
175+
// Waiting time = Turnaround time - Burst time
176+
get<5>(cur) = get<4>(cur) - get<2>(cur);
177+
178+
result.push_back(cur);
179+
schedule.pop();
180+
}
181+
return result;
182+
}
183+
/**
184+
* @brief Utility function for printing the status of each process after
185+
* execution
186+
* @returns void
187+
*/
188+
189+
void printResult(const vector<tuple<S, T, E, double, double, double>>& processes) {
190+
191+
cout << std::setw(17) << left << "Process ID" << std::setw(17) << left
192+
<< "Arrival Time" << std::setw(17) << left << "Burst Time"
193+
<< std::setw(17) << left << "Completion Time" << std::setw(17)
194+
<< left << "Turnaround Time" << std::setw(17) << left
195+
<< "Waiting Time" << endl;
196+
197+
for (const auto& process : processes) {
198+
cout << std::setprecision(2) << std::fixed << std::setw(17) << left
199+
<< get<0>(process) << std::setw(17) << left
200+
<< get<1>(process) << std::setw(17) << left
201+
<< get<2>(process) << std::setw(17) << left
202+
<< get<3>(process) << std::setw(17) << left
203+
<< get<4>(process) << std::setw(17) << left
204+
<< get<5>(process) << endl;
205+
}
206+
}
207+
};
208+
209+
/**
210+
* @brief Computes the final status of processes after applying non-preemptive SJF scheduling
211+
* @tparam S Data type of Process ID
212+
* @tparam T Data type of Arrival time
213+
* @tparam E Data type of Burst time
214+
* @param input A vector of tuples containing Process ID, Arrival time, and Burst time
215+
* @returns A vector of tuples containing Process ID, Arrival time, Burst time,
216+
* Completion time, Turnaround time, and Waiting time
217+
*/
218+
template <typename S, typename T, typename E>
219+
vector<tuple<S, T, E, double, double, double>> get_final_status(
220+
vector<tuple<S, T, E>> input) {
221+
222+
// Sort the processes based on Arrival time and then Burst time
223+
sort(input.begin(), input.end(), sortcol<S, T, E>);
224+
225+
// Result vector to hold the final status of each process
226+
vector<tuple<S, T, E, double, double, double>> result(input.size());
227+
double timeElapsed = 0;
228+
229+
for (size_t i = 0; i < input.size(); i++) {
230+
// Extract Arrival time and Burst time
231+
T arrival = get<1>(input[i]);
232+
E burst = get<2>(input[i]);
233+
234+
// If the CPU is idle, move time to the arrival of the next process
235+
if (arrival > timeElapsed) {
236+
timeElapsed = arrival;
237+
}
238+
239+
// Update timeElapsed by adding the burst time
240+
timeElapsed += burst;
241+
242+
// Calculate Completion time, Turnaround time, and Waiting time
243+
double completion = timeElapsed;
244+
double turnaround = completion - arrival;
245+
double waiting = turnaround - burst;
246+
247+
// Store the results in the result vector
248+
result[i] = make_tuple(get<0>(input[i]), arrival, burst, completion, turnaround, waiting);
249+
}
250+
251+
return result;
252+
}
253+
254+
/**
255+
* @brief Self-test implementations
256+
* @returns void
257+
*/
258+
static void test() {
259+
// A vector to store the results of all processes across all test cases.
260+
vector<tuple<uint32_t, uint32_t, uint32_t, double, double, double>> finalResult;
261+
262+
for (int i{}; i < 10; i++) {
263+
std::random_device rd; // Seeding
264+
std::mt19937 eng(rd());
265+
std::uniform_int_distribution<> distr(1, 10);
266+
267+
uint32_t n = distr(eng);
268+
SJF<uint32_t, uint32_t, uint32_t> readyQueue;
269+
vector<tuple<uint32_t, uint32_t, uint32_t, double, double, double>> input(n);
270+
271+
// Generate random arrival and burst times
272+
for (uint32_t i{}; i < n; i++) {
273+
get<0>(input[i]) = i;
274+
get<1>(input[i]) = distr(eng); // Random arrival time
275+
get<2>(input[i]) = distr(eng); // Random burst time
276+
}
277+
278+
// Print processes before scheduling
279+
cout << "Processes before SJF scheduling:" << endl;
280+
readyQueue.printResult(input);
281+
282+
// Add processes to the queue
283+
for (uint32_t i{}; i < n; i++) {
284+
readyQueue.addProcess(get<0>(input[i]), get<1>(input[i]), get<2>(input[i]));
285+
}
286+
287+
// Perform SJF scheduling
288+
auto finalResult = readyQueue.scheduleForSJF();
289+
290+
// Print processes after scheduling
291+
cout << "\nProcesses after SJF scheduling:" << endl;
292+
readyQueue.printResult(finalResult);
293+
}
294+
cout << "All the tests have successfully passed!" << endl;
295+
}
296+
297+
298+
299+
300+
/**
301+
* @brief Entry point of the program
302+
*/
303+
int main() {
304+
test();
305+
return 0;
306+
}

0 commit comments

Comments
 (0)