Objective

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 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 times 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 times 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 times 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 times in advance (but it knows their distribution function).

Discuss the schedulling disciplines

  1. FCFS FCFS is the simplest scheduling approach, where jobs are processed strictly in their arrival order. Its main advantage is its ease of implementation, as it requires no complex prioritization logic or preemption mechanisms. However FCFS performs poorly in deadline-sensitive environments because it does not account for job urgency. If a long job is being processed, shorter jobs with tight deadlines may be delayed and ultimately fail, even if they could have been completed on time with a smarter policy

  2. EDF This approach is theoretically optimal for minimizing deadline misses, assuming the system is not overloaded. The ability to interrupt lower-priority tasks ensures that urgent jobs are handled promptly. However EDF requires precise knowledge of deadlines and introduces overhead due to frequent preemptions.

  3. EDF-NP EDF-NP follows the same principle as EDF—prioritizing the earliest deadline—but without preemption. This eliminates the overhead of task switching, making it more efficient in some cases. However, since a running job cannot be interrupted, a long task with a late deadline may block a more urgent one, increasing the risk of missed deadlines

  4. SRPT SRPT focuses on maximizing system efficiency by always executing the job with the shortest remaining processing time. This minimizes average response time. However, SRPT ignores deadlines entirely, meaning a short but non-urgent job could delay a longer, deadline-sensitive one. Additionally, it requires exact knowledge of remaining service times, which is often impractical in real-world systems.

  5. SRT2D SRT2D prioritizes jobs based on how close they are to their deadlines, ensuring that tasks in immediate danger of missing their deadlines are executed first. It explicitly considers deadlines. However, like SRPT, it relies on knowing the remaining processing time, which may not always be available. It still struggles in overloaded conditions where too many jobs are near their deadlines simultaneously.

  6. SERT2D SERT2D is a more realistic discipline that uses statistical estimates of remaining service times rather than exact values. SERT2D can make more informed scheduling decisions, reducing the likelihood of deadline misses. However it requires statistical knowledge of service time distributions.

Assumptions

Main code

Here is the main R function that performs the simulation and print the results.

knitr::opts_chunk$set(echo = TRUE)

# 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",
                               "SRT2D",
                               # "EDF-SRPT-D",
                                "SRPT",
                               "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
        ExpectedTimeToDeadline = rep(N, x=NA); #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];
                ExpectedTimeToDeadline[NextArrival] = ES;
                NextArrival = NextArrival + 1;
            }
            
        
            # Job departure
            if(!is.na(CurrentTask)) {
                Remaining[CurrentTask] = Remaining[CurrentTask] - dt;
                ExpectedTimeToDeadline[CurrentTask] = ExpectedTimeToDeadline[CurrentTask] - dt;
                if(!is.na(Remaining[CurrentTask]) && 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 job if they have exceed their deadline
            for (i in WaitingList) {
              if (t  > AbsDeadline[i]) {
                if (!is.na(CurrentTask) && i == CurrentTask){
                  CurrentTask = NA;
                }
                Remaining[i] = 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) {
        
              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 = WaitingList[which.min(Remaining[WaitingList])];
              }
                
              if (scheduling_discipline=="SRT2D"){
                RemainingTimeToDeadline = AbsDeadline[WaitingList]- (t + Remaining[WaitingList]); # calcul le temps restant de tous les jobs
                if (any(RemainingTimeToDeadline > 0)){                # on vérifie qu'il y en ai au moins 1 qui n'a pas vu sa deadline dépassé
                    CurrentTask = WaitingList[which.min(RemainingTimeToDeadline)];  # on choisit le plus petit
                }else{
                  CurrentTask = NA; 
                }
              }
              
              if (scheduling_discipline=="SERT2D"){
                  CurrentTask = WaitingList[which.min(AbsDeadline[WaitingList]-(t+ExpectedTimeToDeadline[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
    # To check if our code is correct, we have the verification with the Little's law that should be verified
    # The average number of jobs in the system should be equal to lambda * R (R is the ResponseTime), 
    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 Simulation

knitr::opts_chunk$set(echo = FALSE)

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

N = 10000; # number of jobs to simulate;

select = rbinom(N,1,0.85);
Service = runif(N,0,10) * select + runif(N,90,110) * (1 - select);

x=5;  Deadline=Service*x; # Relative deadlines

rho=0.9; # load
ES = 0.85 * (0+10)/2 + 0.15 * (90+110)/2; #expected value of the uniform distribution in our case
run_simulation(Service, ES, Deadline, rho);
##  Little law verification:  2.6839 =N=lambda*R= 3.5392 
## FCFS  completed.
##  Little law verification:  2.5091 =N=lambda*R= 2.0421 
## EDF  completed.
##  Little law verification:  2.7312 =N=lambda*R= 3.2572 
## EDF-NP  completed.
##  Little law verification:  2.7859 =N=lambda*R= 2.2487 
## SRT2D  completed.
##  Little law verification:  2.019 =N=lambda*R= 1.55 
## SRPT  completed.
##  Little law verification:  2.6581 =N=lambda*R= 1.9888 
## SERT2D  completed.
## 
## Results:
##              RT    MDR
## FCFS   75.69970 0.5677
## EDF    43.67831 0.0354
## EDF-NP 69.66821 0.4627
## SRT2D  48.09687 0.0939
## SRPT   33.15286 0.0201
## SERT2D 42.53902 0.0326

Analysis of our result

The results show discrepancies between N and λ×R for all scheduling policies, which can be explained by the fact that deadlines and preemptions introduce disturbances. Also jobs removed due to missed deadlines do not contribute to R, slightly skewing the equation.

The gap is particularly noticeable for FCFS (2.68 vs. 3.53) and EDF-NP (2.73 vs. 3.25), suggesting these policies create more unpredictable delays Performance result : - FCFS has the worst performance because ignores deadline - EDF and SRPT have short response times, while having the lowest ratio of missed deadlines. - SRT2D is better than FCFS and EDF-NP but is less efficient than EDF

In conclusion, the choice of policy should align with system priority and available information. For instance, EDF and SERT2D are good for real-time system as SRPT for performance-oriented systems.