📌  相关文章
📜  使用段树的最短作业优先(或SJF)CPU调度非抢占算法

📅  最后修改于: 2021-04-27 17:16:11             🧑  作者: Mango

最短作业优先(SJF)或紧随其后的最短作业是一种调度策略,它选择执行时间最短的等待进程来执行。 SJN是一种非抢占式算法。

  • 最短作业优先的优点是在所有调度算法中平均等待时间最短。
  • 这是一个贪婪算法。
  • 如果持续出现较短的过程,可能会导致饥饿。使用老化的概念可以解决这个问题。
  • 这实际上是不可行的,因为操作系统可能不知道突发时间,因此可能无法对它们进行排序。尽管无法预测执行时间,但是可以使用几种方法来估计作业的执行时间,例如先前执行时间的加权平均值。 SJF可用于可以准确估计运行时间的特殊环境中。

例如:

在上面的示例中,由于所有进程的到达时间均为0,因此进程的执行顺序为进程的突发时间的升序。突发时间由列持续时间给出。因此,流程的执行顺序由下式给出:

P4 -> P1 -> P3 -> P2

这篇文章已经在朴素方法的帮助下讨论了该算法的一种实现。在本文中,该算法是通过使用段树的概念来实现的。

方法:以下是首先执行最短作业的方法:

  1. 顾名思义,最短作业优先算法是执行突发时间最少且已在当前时间之前到达的过程的算法。因此,为了找到需要执行的过程,请根据它们的到达时间从给定的过程集中对所有过程进行排序。这样可以确保首先执行突发时间最短的过程。
  2. 而不是通过迭代来找到所有到达进程中的最小突发时间进程
    在整个struct数组中,使用段树计算所有到达进程的突发时间到当前时间的最小范围。
  3. 选择了需要执行的处理,使用处理的到达时间和突发时间来计算完成时间周转时间等待时间。计算各个时间的公式为:
    • 完成时间:流程完成其执行的时间。
      Completion Time = Start Time + Burst Time
    • 周转时间:完成时间与到达时间之间的时间差。
      Turn Around Time = Completion Time – Arrival Time
    • Waiting Time(WT):等待时间和突发时间之间的时间差。
      Waiting Time = Turn Around Time – Burst Time
  4. 在计算之后,在阵列中更新各个时间,并且在段树基数组中将执行过程的突发时间设置为无穷大,以便在其他查询中将其不视为最小突发时间。

下面是使用段树概念首先实现最短作业的方法:

C++
// C++ implementation of shortest job first
// using the concept of segment tree
  
#include 
using namespace std;
#define ll long long
#define z 1000000007
#define sh 100000
#define pb push_back
#define pr(x) printf("%d ", x)
  
struct util {
  
    // Process ID
    int id;
    // Arrival time
    int at;
    // Burst time
    int bt;
    // Completion time
    int ct;
    // Turnaround time
    int tat;
    // Waiting time
    int wt;
}
  
// Array to store all the process information
// by implementing the above struct util
ar[sh + 1];
  
struct util1 {
  
    // Process id
    int p_id;
    // burst time
    int bt1;
};
  
util1 range;
  
// Segment tree array to
// process the queries in nlogn
util1 tr[4 * sh + 5];
  
// To keep an account of where
// a particular process_id is
// in the segment tree base array
int mp[sh + 1];
  
// Comparator function to sort the
// struct array according to arrival time
bool cmp(util a, util b)
{
    if (a.at == b.at)
        return a.id < b.id;
    return a.at < b.at;
}
  
// Function to update the burst time and process id
// in the segment tree
void update(int node, int st, int end,
            int ind, int id1, int b_t)
{
    if (st == end) {
        tr[node].p_id = id1;
        tr[node].bt1 = b_t;
        return;
    }
    int mid = (st + end) / 2;
    if (ind <= mid)
        update(2 * node, st, mid, ind, id1, b_t);
    else
        update(2 * node + 1, mid + 1, end, ind, id1, b_t);
    if (tr[2 * node].bt1 < tr[2 * node + 1].bt1) {
        tr[node].bt1 = tr[2 * node].bt1;
        tr[node].p_id = tr[2 * node].p_id;
    }
    else {
        tr[node].bt1 = tr[2 * node + 1].bt1;
        tr[node].p_id = tr[2 * node + 1].p_id;
    }
}
  
// Function to return the range minimum of the burst time
// of all the arrived processes using segment tree
util1 query(int node, int st, int end, int lt, int rt)
{
    if (end < lt || st > rt)
        return range;
    if (st >= lt && end <= rt)
        return tr[node];
    int mid = (st + end) / 2;
    util1 lm = query(2 * node, st, mid, lt, rt);
    util1 rm = query(2 * node + 1, mid + 1, end, lt, rt);
    if (lm.bt1 < rm.bt1)
        return lm;
    return rm;
}
  
// Function to perform non_preemptive
// shortest job first and return the
// completion time, turn around time and
// waiting time for the given processes
void non_premptive_sjf(int n)
{
  
    // To store the number of processes
    // that have been completed
    int counter = n;
  
    // To keep an account of the number
    // of processes that have been arrived
    int upper_range = 0;
  
    // Current running time
    int tm = min(INT_MAX, ar[upper_range + 1].at);
  
    // To find the list of processes whose arrival time
    // is less than or equal to the current time
    while (counter) {
        for (; upper_range <= n;) {
            upper_range++;
            if (ar[upper_range].at > tm || upper_range > n) {
                upper_range--;
                break;
            }
  
            update(1, 1, n, upper_range,
                   ar[upper_range].id, ar[upper_range].bt);
        }
  
        // To find the minimum of all the running times
        // from the set of processes whose arrival time is
        // less than or equal to the current time
        util1 res = query(1, 1, n, 1, upper_range);
  
        // Checking if the process has already been executed
        if (res.bt1 != INT_MAX) {
            counter--;
            int index = mp[res.p_id];
            tm += (res.bt1);
  
            // Calculating and updating the array with
            // the current time, turn around time and waiting time
            ar[index].ct = tm;
            ar[index].tat = ar[index].ct - ar[index].at;
            ar[index].wt = ar[index].tat - ar[index].bt;
  
            // Update the process burst time with
            // infinity when the process is executed
            update(1, 1, n, index, INT_MAX, INT_MAX);
        }
        else {
            tm = ar[upper_range + 1].at;
        }
    }
}
  
// Function to call the functions and perform
// shortest job first operation
void execute(int n)
{
  
    // Sort the array based on the arrival times
    sort(ar + 1, ar + n + 1, cmp);
    for (int i = 1; i <= n; i++)
        mp[ar[i].id] = i;
  
    // Calling the function to perform
    // non-premptive-sjf
    non_premptive_sjf(n);
}
  
// Function to print the required values after
// performing shortest job first
void print(int n)
{
  
    cout << "ProcessId  "
         << "Arrival Time  "
         << "Burst Time  "
         << "Completion Time  "
         << "Turn Around Time  "
         << "Waiting Time\n";
    for (int i = 1; i <= n; i++) {
        cout << ar[i].id << " \t\t "
             << ar[i].at << " \t\t "
             << ar[i].bt << " \t\t "
             << ar[i].ct << " \t\t "
             << ar[i].tat << " \t\t "
             << ar[i].wt << " \n";
    }
}
  
// Driver code
int main()
{
    // Number of processes
    int n = 5;
  
    // Initializing the process id
    // and burst time
    range.p_id = INT_MAX;
    range.bt1 = INT_MAX;
  
    for (int i = 1; i <= 4 * sh + 1; i++) {
        tr[i].p_id = INT_MAX;
        tr[i].bt1 = INT_MAX;
    }
  
    // Arrival time, Burst time and ID
    // of the processes on which SJF needs
    // to be performed
    ar[1].at = 1;
    ar[1].bt = 7;
    ar[1].id = 1;
  
    ar[2].at = 2;
    ar[2].bt = 5;
    ar[2].id = 2;
  
    ar[3].at = 3;
    ar[3].bt = 1;
    ar[3].id = 3;
  
    ar[4].at = 4;
    ar[4].bt = 2;
    ar[4].id = 4;
  
    ar[5].at = 5;
    ar[5].bt = 8;
    ar[5].id = 5;
  
    execute(n);
  
    // Print the calculated time
    print(n);
}


Java
// Java implementation of shortest job first 
// using the concept of segment tree 
import java.util.*;
  
class GFG {
  
    static int z = 1000000007;
    static int sh = 100000;
  
    static class util {
  
        // Process ID
        int id;
        // Arrival time
        int at;
        // Burst time
        int bt;
        // Completion time
        int ct;
        // Turnaround time
        int tat;
        // Waiting time
        int wt;
    }
  
    // Array to store all the process information
    // by implementing the above struct util
    static util[] ar = new util[sh + 1];
    static {
        for (int i = 0; i < sh + 1; i++) {
            ar[i] = new util();
        }
    }
  
    static class util1 {
  
        // Process id
        int p_id;
        // burst time
        int bt1;
    };
  
    static util1 range = new util1();
  
    // Segment tree array to
    // process the queries in nlogn
    static util1[] tr = new util1[4 * sh + 5];
    static {
        for (int i = 0; i < 4 * sh + 5; i++) {
            tr[i] = new util1();
        }
    }
  
    // To keep an account of where
    // a particular process_id is
    // in the segment tree base array
    static int[] mp = new int[sh + 1];
  
    // Comparator function to sort the
    // struct array according to arrival time
  
    // Function to update the burst time and process id
    // in the segment tree
    static void update(int node, int st, int end, 
                        int ind, int id1, int b_t)
    {
        if (st == end) {
            tr[node].p_id = id1;
            tr[node].bt1 = b_t;
            return;
        }
        int mid = (st + end) / 2;
        if (ind <= mid)
            update(2 * node, st, mid, ind, id1, b_t);
        else
            update(2 * node + 1, mid + 1, end, ind, id1, b_t);
        if (tr[2 * node].bt1 < tr[2 * node + 1].bt1) {
            tr[node].bt1 = tr[2 * node].bt1;
            tr[node].p_id = tr[2 * node].p_id;
        } else {
            tr[node].bt1 = tr[2 * node + 1].bt1;
            tr[node].p_id = tr[2 * node + 1].p_id;
        }
    }
  
    // Function to return the range minimum of the burst time
    // of all the arrived processes using segment tree
    static util1 query(int node, int st, int end,
                        int lt, int rt) 
    {
        if (end < lt || st > rt)
            return range;
        if (st >= lt && end <= rt)
            return tr[node];
        int mid = (st + end) / 2;
        util1 lm = query(2 * node, st, mid, lt, rt);
        util1 rm = query(2 * node + 1, mid + 1, end, lt, rt);
        if (lm.bt1 < rm.bt1)
            return lm;
        return rm;
    }
  
    // Function to perform non_preemptive
    // shortest job first and return the
    // completion time, turn around time and
    // waiting time for the given processes
    static void non_premptive_sjf(int n) {
  
        // To store the number of processes
        // that have been completed
        int counter = n;
  
        // To keep an account of the number
        // of processes that have been arrived
        int upper_range = 0;
  
        // Current running time
        int tm = Math.min(Integer.MAX_VALUE, ar[upper_range + 1].at);
  
        // To find the list of processes whose arrival time
        // is less than or equal to the current time
        while (counter != 0) {
            for (; upper_range <= n;) {
                upper_range++;
                if (ar[upper_range].at > tm || upper_range > n) {
                    upper_range--;
                    break;
                }
  
                update(1, 1, n, upper_range, ar[upper_range].id, 
                        ar[upper_range].bt);
            }
  
            // To find the minimum of all the running times
            // from the set of processes whose arrival time is
            // less than or equal to the current time
            util1 res = query(1, 1, n, 1, upper_range);
  
            // Checking if the process has already been executed
            if (res.bt1 != Integer.MAX_VALUE) {
                counter--;
                int index = mp[res.p_id];
                tm += (res.bt1);
  
                // Calculating and updating the array with
                // the current time, turn around time and waiting time
                ar[index].ct = tm;
                ar[index].tat = ar[index].ct - ar[index].at;
                ar[index].wt = ar[index].tat - ar[index].bt;
  
                // Update the process burst time with
                // infinity when the process is executed
                update(1, 1, n, index, Integer.MAX_VALUE, Integer.MAX_VALUE);
            } else {
                tm = ar[upper_range + 1].at;
            }
        }
    }
  
    // Function to call the functions and perform
    // shortest job first operation
    static void execute(int n) {
  
        // Sort the array based on the arrival times
        Arrays.sort(ar, 1, n, new Comparator() {
            public int compare(util a, util b) {
                if (a.at == b.at)
                    return a.id - b.id;
                return a.at - b.at;
            }
        });
        for (int i = 1; i <= n; i++)
            mp[ar[i].id] = i;
  
        // Calling the function to perform
        // non-premptive-sjf
        non_premptive_sjf(n);
    }
  
    // Function to print the required values after
    // performing shortest job first
    static void print(int n) {
  
        System.out.println("ProcessId Arrival Time Burst Time" +
                " Completion Time Turn Around Time Waiting Time");
        for (int i = 1; i <= n; i++) {
            System.out.printf("%d\t\t%d\t\t%d\t\t%d\t\t%d\t\t%d\n", 
                 ar[i].id, ar[i].at, ar[i].bt, ar[i].ct, ar[i].tat,
                    ar[i].wt);
        }
    }
  
    // Driver Code
    public static void main(String[] args) 
    {
        // Number of processes
        int n = 5;
  
        // Initializing the process id
        // and burst time
        range.p_id = Integer.MAX_VALUE;
        range.bt1 = Integer.MAX_VALUE;
  
        for (int i = 1; i <= 4 * sh + 1; i++)
        {
            tr[i].p_id = Integer.MAX_VALUE;
            tr[i].bt1 = Integer.MAX_VALUE;
        }
  
        // Arrival time, Burst time and ID
        // of the processes on which SJF needs
        // to be performed
        ar[1].at = 1;
        ar[1].bt = 7;
        ar[1].id = 1;
  
        ar[2].at = 2;
        ar[2].bt = 5;
        ar[2].id = 2;
  
        ar[3].at = 3;
        ar[3].bt = 1;
        ar[3].id = 3;
  
        ar[4].at = 4;
        ar[4].bt = 2;
        ar[4].id = 4;
  
        ar[5].at = 5;
        ar[5].bt = 8;
        ar[5].id = 5;
  
        execute(n);
  
        // Print the calculated time
        print(n);
    }
}
  
// This code is contributed by
// sanjeev2552


输出:
ProcessId  Arrival Time  Burst Time  Completion Time  Turn Around Time  Waiting Time
1          1          7          8          7          0 
2          2          5          16          14          9 
3          3          1          9          6          5 
4          4          2          11          7          5 
5          5          8          24          19          11

时间复杂度:为了分析上述算法的运行时间,首先需要了解以下运行时间:

  • 构造用于N个进程的分段树的时间复杂度为O(N)
  • O(log(N))给出了更新段树中节点的时间复杂度。
  • O(log(N))给出在段树中执行范围最小查询的时间复杂度。
  • 由于更新操作和查询是针对给定的N个进程执行的,因此该算法的总时间复杂度为O(N * log(N)) ,其中N为进程数。
  • 该算法的性能比本文中提到的方法更好,因为它需要O(N 2 )来执行。