Problem statement

The goal of this homework assignment is to simulate and analyze the dynamics of a G/G/1 queue under various scheduling disciplines, where jobs have hard deadlines. Specifically, each arriving job must be completed before its individual deadline. The deadline is considered “hard” because if it is not met, the job becomes useless and is consequently removed from the system.

We consider the following scheduling disciplines:

  1. FCFS: jobs are processed following the order of their arrival. If a job’s deadline expires while it is being processed, the job is removed from the system. The server does not know the service time in advance.

  2. EDF (Earliest Deadline First): jobs are processed according to the earliest deadline with preemption, i.e., the current job is interrupted if a new job with an earlier deadline arrives. The server does not know the service time in advance.

  3. EDF-NP (Earliest Deadline First Non-preemptive): As EDF but jobs are not interrupted once their processing has started. The server does not know the service time in advance.

  4. SRPT (Shortest Remaining Processing Time): the job with the shortest remaining processing time is served first. Here, the jobs whose remaining processing times overflow their corresponding deadlines are not considered, i.e., removed from the system.

  5. SRT2D (Shortest Remaining Time to Deadline): the job with the shortest remaining time to deadline is served first. Note that “the remaining time to deadline” is always non-negative because jobs that cannot be completed by the deadline are removed from the system.

  6. SERT2D (Shortest Expected Remaining Time to Deadline): as SRT2D but instead of the remaining time to deadline, we consider the expected remaining time to deadline. The server does not know the service time in advance.

Discussing the scheduling disciplines

  1. FCFS This scheduling discipline is easy to implement as it requires very little information overall. However, because so little information is taken into account, this discipline might ignore jobs that should be treated as a priority, and a lot of deadlines could be missed.

  2. EDF This discipline takes the deadline into account, and will probably miss fewer deadlines, while still being pretty simple to implement. It may however give priority to jobs whose deadlines are impossible to meet, thus wasting resources.

  3. EDF-NP Similar to EDF. With this discipline, some jobs may never be able to reach their deadline if they arrive after the current job and their deadline are earlier than the current job’s.

  4. SRPT This discipline is more complex to implement, especially in a real system, since the time a job would take to process must be known in advance. If we have access to this information, more jobs will be processed as longer jobs wouldn’t prevent shorter jobs to reach their deadlines. It would work especially well in systems where there are many short jobs and few longer jobs.

  5. SRT2D This discipline requires the most information among those we study in this assignment, as it requires both information about the deadline and the time jobs would require. It’s probably the most unrealistic one to implement into real systems.

  6. SERT2D This one is similar to SRT2D, but requires fewer information. Implementing it into a real system is probably feasible, while having similar efficiency to SRT2D.

Main code

Here is the main R function that performs the simulation and print the results. (this code is heavily based on the code from TD #2 and #3)

knitr::opts_chunk$set(echo = TRUE)

set.seed(9999); # Set seed for reproducibility

# This function performs the required simulation.
# As input, it takes the job service times (Service), their expected value (ES), the job deadlines (relative to the absolute job arrival time), and the system load
run_simulation <- function(Service, ES, Deadline, rho){
    debug=0;
    scheduling_disciplines = c("FCFS",
                               "EDF",
                               "EDF-NP",
                               "SRPT",
                               "SRT2D",
                               "SERT2D"
                               );
    num_arrival_rates=1;
    lambda = rho/ES;
    
    # Matrix of average response times for all disciplines and arrival rates
    Results <- matrix(0, nrow = length(scheduling_disciplines), ncol = 2);
    rownames(Results) <- scheduling_disciplines;
    colnames(Results) <- c("RT", "MDR"); # performance metrics
    
    # Let's assume a Poisson process coming in, otherwise change the next line
    Arrival= cumsum(rexp(n=N, rate = lambda)); # Arrival times
    AbsDeadline = Arrival+Deadline;
    
    for (scheduling_discipline in scheduling_disciplines)
    {
        t = 0; # simulation time
        Remaining = rep(N, x=NA);    # Remaining service times of each job
        Completion = rep(N, x=NA);   # Completion time of each job
        ExpectedRemaining = rep(N,x=NA); # Expected remaining service time (used for SERT2D)
        
        CurrentTask = NA;
        NextArrival = 1;
        AvgJobs = 0;
        MDR = 0;
        while (TRUE) {

if (debug==1){
    print("*********************");
    cat("Arrival:     ",Arrival,'\n');
    cat("AbsDeadline: ",AbsDeadline,'\n');
    cat("Service:     ",Service,'\n');
    cat("Remaining:   ",Remaining,'\n');
}
            dtA = NA; dtC = NA;
            if(length(Arrival[Arrival>t])>0) { # if an arrival exists after t
                dtA = head(Arrival[Arrival>t],n=1)-t ; # time to next arrival
            }
            if(!is.na(CurrentTask)) { # if a task is running
                dtC = Remaining[CurrentTask]; # time to next completion
            }
            if(is.na(dtA) & is.na(dtC)) {
                break;
            } 
        
            dt = min(dtA,dtC,na.rm=T);
        
            # update system variables
            t = t + dt;
            AvgJobs=AvgJobs+dt*sum(!is.na(Remaining));
            
            # Job arrival
            if((NextArrival <=N) & (Arrival[NextArrival] == t)) {
                Remaining[NextArrival] = Service[NextArrival];
                ExpectedRemaining[NextArrival] = ES;
                NextArrival = NextArrival + 1;
            }
        
            # Job departure
            if(!is.na(CurrentTask)) {
                Remaining[CurrentTask] = Remaining[CurrentTask] - dt;
                ExpectedRemaining[CurrentTask] = ExpectedRemaining[CurrentTask] - dt;
                if(Remaining[CurrentTask] <= 0) {
                    # CurrentTask completed
                    Completion[CurrentTask] = t;
                    Remaining[CurrentTask] = NA;
                    CurrentTask = NA;
                }
            }
            
            # Scheduling discipline: Let's find the new task to process
            WaitingList=(1:NextArrival)[!is.na(Remaining)];

             # Remove jobs from the system if they can't reach their deadline
            for(Task in WaitingList) {
                if(AbsDeadline[Task] <= t + Remaining[Task]) {
                    if(!is.na(CurrentTask) && Task == CurrentTask) {
                        CurrentTask = NA;
                    }
                    Remaining[Task] = NA;
                    MDR = MDR + 1;
                }
            }
            WaitingList=(1:NextArrival)[!is.na(Remaining)];
           
if (debug==1){
    print(paste("Sim. time:", t));
    print(paste("# of jobs arrived: ", NextArrival));
    cat("WaitingList:", WaitingList, "\n");
}
            if(length(WaitingList)>0) {
        
              # STEP 1: Scheduling disciplines implementation 
              
              if (scheduling_discipline=="FCFS"){
                CurrentTask = head(WaitingList,n=1);
              }
              
              if (scheduling_discipline=="EDF"){
                 # Solution 1
                 # CurrentTask = which(AbsDeadline==min(AbsDeadline[WaitingList]));
                 # Solution 2
                  CurrentTask = WaitingList[which.min(AbsDeadline[WaitingList])];
                 # cat(CurrentTask, " while FCFS says ", head(WaitingList,n=1), '\n');
              }
        
              if (scheduling_discipline=="EDF-NP"){
                  if (is.na(CurrentTask)) {
                    CurrentTask = WaitingList[which.min(AbsDeadline[WaitingList])];
                  }
              }

              if (scheduling_discipline=="SRPT"){
                    CurrentTask = which.min(Remaining);
              }
                
              if (scheduling_discipline=="SRT2D"){
                    CurrentTask = WaitingList[which.min(AbsDeadline[WaitingList]-(t+Remaining[WaitingList]))];
              }
              
              if (scheduling_discipline=="SERT2D"){
                  CurrentTask = WaitingList[which.min(AbsDeadline[WaitingList]-(t+ExpectedRemaining[WaitingList]))];
              }

            }
    
if (debug==1){
    print(paste("Current Task = ", CurrentTask))
    #    readline(prompt="Press [enter] to proceed")
}
        
    } # while
    
        # STEP 2: Performance measures implementations
        MDR=MDR/length(Arrival);
        ResponseTime = mean((Completion-Arrival)[!is.na(Completion)]);
        
        
        AvgJobs=AvgJobs/(tail(Completion[!is.na(Completion)],n=1)-Arrival[1]);

if (debug>=0){
    # STEP 3: Code correctness
    # If our code is correct, Little's law should be verified:
    # The average number of jobs in the system should be equal to lambda * R, 
    # Where R is the ResponseTime of the system.
    cat(paste(" Little law verification: ", round(AvgJobs,digits = 4), "=N=lambda*R=", 
              round(lambda*ResponseTime,digits = 4),"\n"));
}        
        
        # STEP 4: Performance evaluation (creation of the table)
        # Simulation completed.
        Results[scheduling_discipline,1]=ResponseTime;
        Results[scheduling_discipline,2]=MDR;
        
        cat(scheduling_discipline," completed.\n");
    
    } # loop scheduling discipline
    
    cat("\nResults:\n")
    print(Results)
    
} # end function

Running the simulation

knitr::opts_chunk$set(echo = FALSE)

N = 1e4; # number of jobs to simulate;


select = rbinom(N,1,0.85); Service = (runif(N,0,10) * select) + (runif(N,90,110) * (1 - select)); # Service times
x=5;
Deadline=Service*x; # Relative deadlines

rho=0.9; # load
ES=((0+10)/2)*0.85 + ((90+110)/2)*0.15;
run_simulation(Service, ES, Deadline, rho)
##  Little law verification:  2.4292 =N=lambda*R= 3.227 
## FCFS  completed.
##  Little law verification:  2.1371 =N=lambda*R= 1.9152 
## EDF  completed.
##  Little law verification:  2.417 =N=lambda*R= 2.9986 
## EDF-NP  completed.
##  Little law verification:  1.83 =N=lambda*R= 1.5041 
## SRPT  completed.
##  Little law verification:  2.4345 =N=lambda*R= 2.1287 
## SRT2D  completed.
##  Little law verification:  2.3752 =N=lambda*R= 1.9807 
## SERT2D  completed.
## 
## Results:
##              RT    MDR
## FCFS   69.02212 0.5543
## EDF    40.96385 0.0124
## EDF-NP 64.13603 0.4708
## SRPT   32.17165 0.0168
## SRT2D  45.53099 0.0340
## SERT2D 42.36482 0.0197

Analysis of our results

  • Code correctness:
    • Little’s law seems to be verified for all of the scheduling disciplines, as the average number of jobs in the system is close to its theoretical value. We can thus assume our simulation is correct.
  • Performance results:
    • EDF and SRPT both have short response times, while having the lowest ratio of missed deadlines.
    • Both SRT2D and SERT2D have low missed deadline ratios, but their response time is slightly longer than SRPT.
    • FCFS and EDF-NP both have long response times, while missing about half of their deadlines.

SRPT seems to be the best scheduling discipline overall in terms of response time while missing very few deadlines. EDF has a slightly worse response time but seems to miss fewer deadlines than SRPT. SRT2D and SERT2D also have solid results but perform worse than SRPT and EDF. Finally, FCFS and EDF-NP’s performances are pretty bad both from a response time and from a missed deadline ratio standpoint.

In different conditions, these disciplines might perform differently. For instance, if deadlines weren’t strict, SRT2D and SERT2D might miss their deadlines, but with very low lateness.