knitr::opts_chunk$set(echo = TRUE)
# This function plot the average response time of a M/G/k queue for various scheduling disciplines.
# As input, it takes the job service times (Service), their mean value (ES), the number of server (k) and the debug we want (0=no debug, 1=full debug and 2=debug of FCFS with highest lambda)
plot_response_times <- function(Service, ES, k, debug){
scheduling_disciplines = c(1:4); # 'FCFS'=1,'LCFS'=2,'RO-S'=3,'SRPT'=4
num_arrival_rates=5;
lambdas = (1/ES) * c(1:num_arrival_rates)/(num_arrival_rates+1) ;
# Matrix of average response times for all disciplines and arrival rates
R_time <- matrix(0, nrow = length(scheduling_disciplines), ncol = length(lambdas));
rownames(R_time) <- c("FCFS","LCFS","RO-S","SRPT");
colnames(R_time) <- paste("rho=", round(lambdas*ES, digits = 3), sep = "");
infoPrinted = 0;
cnt=0;
for (lambda in lambdas)
{
cnt=cnt+1;
# Let's assume a Poisson process coming in, otherwise change the next line
Arrival= cumsum(rexp(n=N, rate = lambda)); # Arrival times
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
AvgJobs = 0;
CurrentTask = rep(k, x=NA);
NextArrival = 1;
while (TRUE) {
if (debug==1 | (debug ==2 & lambda==lambdas[num_arrival_rates] & scheduling_discipline==1)){
print("******************************************************************************************************");
if (infoPrinted == 0) { # We only print the arrival and service times once
cat("Arrival: "); print(Arrival);
cat("Service: "); print(Service);
infoPrinted = 1;
}
cat("Remaining: "); print(Remaining);
cat("Completion: "); print(Completion);
}
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
}
# We update dtC to be the shortest time until completion
for (i in 1:length(CurrentTask)) {
if(!is.na(CurrentTask[i])) { # if a task is running
if (is.na(dtC)) {
dtC = Remaining[CurrentTask[i]]; # time to next completion
} else if (dtC > Remaining[CurrentTask[i]]) {
dtC = Remaining[CurrentTask[i]]; # time to next completion
}
}
}
# no more arrival / completion
if(is.na(dtA) & is.na(dtC)) {
break;
}
# Next event
dt = min(dtA,dtC,na.rm=T);
# update system variables
t = t + dt;
AvgJobs=AvgJobs+dt*sum(!is.na(Remaining));
if (debug==1 | (debug ==2 & lambda==lambdas[num_arrival_rates] & scheduling_discipline==1)){
print(paste("Sim. time:", t));
print(paste("# of jobs arrived: ", NextArrival));
}
# Job arrival
if((NextArrival <=N) & (Arrival[NextArrival] == t)) {
Remaining[NextArrival] = Service[NextArrival];
NextArrival = NextArrival + 1;
}
# Job departure
for (i in 1:length(CurrentTask)) {
if(!is.na(CurrentTask[i])) {
Remaining[CurrentTask[i]] = Remaining[CurrentTask[i]] - dt ;
if(Remaining[CurrentTask[i]] <= 0) { # Remove finished task
# CurrentTask completed
Completion[CurrentTask[i]] = t;
Remaining[CurrentTask[i]] = NA;
CurrentTask[i] = NA;
}
}
}
# Scheduling discipline
WaitingList=(1:NextArrival)[!is.na(Remaining)];
# We only take the jobs waiting not the one being processed
for (i in CurrentTask) {
if (!is.na(i)) {
WaitingList <- WaitingList[WaitingList != i];
}
}
if(length(WaitingList)>0) {
# FCFS
if (scheduling_discipline==1){
for (i in 1:length(CurrentTask)) {
if (is.na(CurrentTask[i])) {
CurrentTask[i] = head(WaitingList, n=1);
break;
}
}
}
# LCFS
if (scheduling_discipline==2){
for (i in 1:length(CurrentTask)) {
if (is.na(CurrentTask[i])) {
CurrentTask[i] = tail(WaitingList, n=1);
break;
}
}
}
# ROS: Random Order of Service
if (scheduling_discipline==3){
for (i in 1:length(CurrentTask)) {
if(is.na(CurrentTask[i])) {
# here, a task is not running, so we find a new job to serve
if (length(WaitingList)>1) {
CurrentTask[i] = sample(WaitingList,size=1);
} else{
CurrentTask[i] = WaitingList;
}
break;
}
}
}
# SRPT: Shortest Remaining Processing Time
if (scheduling_discipline==4){
for (i in 1:length(CurrentTask)) {
if(is.na(CurrentTask[i])) {
if (length(CurrentTask[!is.na(CurrentTask)]) > 0) { # If at least one task is being processed
tab = Remaining;
tab[CurrentTask[!is.na(CurrentTask)]] <- NA; # Set the service times of tasks currently being processed to NA
CurrentTask[i] = which.min(tab); # Take the task with the minimal remaining time
break;
} else {
CurrentTask[i] = which.min(Remaining);
break;
}
}
}
}
}
if (debug==1 | (debug ==2 & lambda==lambdas[num_arrival_rates] & scheduling_discipline==1)){
for (i in 1:length(CurrentTask)) {
print(paste("Current Task = ", CurrentTask[i]))
}
if (debug==1) {
readline(prompt="Press [enter] to proceed")
}
}
} # while
if (debug != 2) {
ResponseTime = mean(Completion-Arrival); #average response time
AvgJobs=AvgJobs/(tail(Completion,n=1)-Arrival[1]);
# Simulation completed. Let's verify Little law: N=lambda*R
r_names=rownames(R_time)
cat(paste("Sim. ",r_names[scheduling_discipline],"(rho=",round(lambda*ES,digits = 2),") completed."));
cat(paste(" Little law verification: ", round(AvgJobs,digits = 4), "=N=lambda*R=", round(lambda*ResponseTime,digits = 4),"\n"));
R_time[scheduling_discipline,cnt]=ResponseTime;
}
} # loop scheduling discipline
} # loop lambda
if (debug != 2) {
cat("\nResponse time matrix:\n")
print(R_time)
matplot(t(R_time), type = "l", xlab="Load (lambda*E[S])", ylab="Avg Response Time", xaxt = "n", lwd = 3);
legend("topleft", legend = rownames(R_time), col=1:4, pch=1);
axis(side=1,at=1:ncol(R_time),labels=round(lambdas*ES, digits=3));
}
return (R_time)
} # end function
To verify that the execution is correct we observe the simulation over an execution using k = 3, N = 10 and the FCFS scheduling discipline:
knitr::opts_chunk$set(echo = FALSE)
set.seed(11); # Set seed for reproducibility
N = 10; # number of jobs to simulate;
k = 3; # number of servers
mu=1;
Service=rexp(N,rate=mu/k); # Service times
plot_response_times(Service, 1/mu, k, 2);
## [1] "******************************************************************************************************"
## Arrival: [1] 0.6350583 6.0219460 7.8460552 8.0548722 10.2816795 11.8768878
## [7] 13.4711646 15.9174554 16.8163547 17.2756400
## Service: [1] 2.40643907 20.97892994 0.06365024 12.61116786 7.39012740 2.55914165
## [7] 0.25622089 3.84913335 1.40392496 0.44211433
## Remaining: [1] NA NA NA NA NA NA NA NA NA NA
## Completion: [1] NA NA NA NA NA NA NA NA NA NA
## [1] "Sim. time: 0.635058255493641"
## [1] "# of jobs arrived: 1"
## [1] "Current Task = 1"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] 2.406439 NA NA NA NA NA NA NA
## [9] NA NA
## Completion: [1] NA NA NA NA NA NA NA NA NA NA
## [1] "Sim. time: 3.0414973278201"
## [1] "# of jobs arrived: 2"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA NA NA NA NA NA NA NA NA NA
## Completion: [1] 3.041497 NA NA NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 6.02194598370303"
## [1] "# of jobs arrived: 2"
## [1] "Current Task = 2"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 20.97893 NA NA NA NA NA NA
## [9] NA NA
## Completion: [1] 3.041497 NA NA NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 7.84605523825728"
## [1] "# of jobs arrived: 3"
## [1] "Current Task = 2"
## [1] "Current Task = 3"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 19.15482068 0.06365024 NA NA NA
## [7] NA NA NA NA
## Completion: [1] 3.041497 NA NA NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 7.9097054761193"
## [1] "# of jobs arrived: 4"
## [1] "Current Task = 2"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 19.09117 NA NA NA NA NA NA
## [9] NA NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 8.05487217989855"
## [1] "# of jobs arrived: 4"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 18.94600 NA 12.61117 NA NA NA NA
## [9] NA NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 10.2816795317229"
## [1] "# of jobs arrived: 5"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 5"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 16.719196 NA 10.384361 7.390127 NA NA
## [8] NA NA NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 11.8768877838939"
## [1] "# of jobs arrived: 6"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 5"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 15.123988 NA 8.789152 5.794919 2.559142 NA
## [8] NA NA NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 13.4711646479251"
## [1] "# of jobs arrived: 7"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 5"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 13.5297113 NA 7.1948754 4.2006423 2.5591417
## [7] 0.2562209 NA NA NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 15.9174554448194"
## [1] "# of jobs arrived: 8"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 5"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 11.0834205 NA 4.7485846 1.7543515 2.5591417
## [7] 0.2562209 3.8491334 NA NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 16.816354745538"
## [1] "# of jobs arrived: 9"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 5"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 10.1845212 NA 3.8496853 0.8554522 2.5591417
## [7] 0.2562209 3.8491334 1.4039250 NA
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 17.2756400021771"
## [1] "# of jobs arrived: 10"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 5"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 9.7252359 NA 3.3904000 0.3961669 2.5591417 0.2562209
## [8] 3.8491334 1.4039250 0.4421143
## Completion: [1] 3.041497 NA 7.909705 NA NA NA NA NA
## [9] NA NA
## [1] "Sim. time: 17.6718069350065"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 6"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 9.3290690 NA 2.9942331 NA 2.5591417 0.2562209
## [8] 3.8491334 1.4039250 0.4421143
## Completion: [1] 3.041497 NA 7.909705 NA 17.671807 NA NA
## [8] NA NA NA
## [1] "Sim. time: 20.2309485866077"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 7"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 6.7699273 NA 0.4350915 NA NA 0.2562209
## [8] 3.8491334 1.4039250 0.4421143
## Completion: [1] 3.041497 NA 7.909705 NA 17.671807 20.230949 NA
## [8] NA NA NA
## [1] "Sim. time: 20.4871694742372"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = 4"
## [1] "Current Task = 8"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 6.5137064 NA 0.1788706 NA NA NA
## [8] 3.8491334 1.4039250 0.4421143
## Completion: [1] 3.041497 NA 7.909705 NA 17.671807 20.230949 20.487169
## [8] NA NA NA
## [1] "Sim. time: 20.6660400388185"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = 9"
## [1] "Current Task = 8"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 6.3348359 NA NA NA NA NA
## [8] 3.6702628 1.4039250 0.4421143
## Completion: [1] 3.041497 NA 7.909705 20.666040 17.671807 20.230949 20.487169
## [8] NA NA NA
## [1] "Sim. time: 22.0699649952706"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = 10"
## [1] "Current Task = 8"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 4.9309109 NA NA NA NA NA
## [8] 2.2663378 NA 0.4421143
## Completion: [1] 3.041497 NA 7.909705 20.666040 17.671807 20.230949 20.487169
## [8] NA 22.069965 NA
## [1] "Sim. time: 22.5120793242361"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = NA"
## [1] "Current Task = 8"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 4.488797 NA NA NA NA NA 1.824224
## [9] NA NA
## Completion: [1] 3.041497 NA 7.909705 20.666040 17.671807 20.230949 20.487169
## [8] NA 22.069965 22.512079
## [1] "Sim. time: 24.3363028277244"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = 2"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA 2.664573 NA NA NA NA NA NA
## [9] NA NA
## Completion: [1] 3.041497 NA 7.909705 20.666040 17.671807 20.230949 20.487169
## [8] 24.336303 22.069965 22.512079
## [1] "Sim. time: 27.0008759230539"
## [1] "# of jobs arrived: 11"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "Current Task = NA"
## [1] "******************************************************************************************************"
## Remaining: [1] NA NA NA NA NA NA NA NA NA NA
## Completion: [1] 3.041497 27.000876 7.909705 20.666040 17.671807 20.230949 20.487169
## [8] 24.336303 22.069965 22.512079
## rho=0.167 rho=0.333 rho=0.5 rho=0.667 rho=0.833
## FCFS 0 0 0 0 0
## LCFS 0 0 0 0 0
## RO-S 0 0 0 0 0
## SRPT 0 0 0 0 0
We can clearly see that during the execution the 3 servers are being used, and that the task entering a server is always the oldest one (which is expected using a FCFS scheduling).
We now want to compare a G/G/k queue with a G/G/1 queue having a server k times faster:
## Sim. FCFS (rho= 0.17 ) completed. Little law verification: 0.1654 =N=lambda*R= 0.1688
## Sim. LCFS (rho= 0.17 ) completed. Little law verification: 0.1654 =N=lambda*R= 0.1688
## Sim. RO-S (rho= 0.17 ) completed. Little law verification: 0.1654 =N=lambda*R= 0.1688
## Sim. SRPT (rho= 0.17 ) completed. Little law verification: 0.1654 =N=lambda*R= 0.1688
## Sim. FCFS (rho= 0.33 ) completed. Little law verification: 0.346 =N=lambda*R= 0.3389
## Sim. LCFS (rho= 0.33 ) completed. Little law verification: 0.346 =N=lambda*R= 0.339
## Sim. RO-S (rho= 0.33 ) completed. Little law verification: 0.346 =N=lambda*R= 0.339
## Sim. SRPT (rho= 0.33 ) completed. Little law verification: 0.346 =N=lambda*R= 0.3389
## Sim. FCFS (rho= 0.5 ) completed. Little law verification: 0.4952 =N=lambda*R= 0.5098
## Sim. LCFS (rho= 0.5 ) completed. Little law verification: 0.4949 =N=lambda*R= 0.5095
## Sim. RO-S (rho= 0.5 ) completed. Little law verification: 0.4954 =N=lambda*R= 0.51
## Sim. SRPT (rho= 0.5 ) completed. Little law verification: 0.4948 =N=lambda*R= 0.5094
## Sim. FCFS (rho= 0.67 ) completed. Little law verification: 0.6543 =N=lambda*R= 0.6824
## Sim. LCFS (rho= 0.67 ) completed. Little law verification: 0.6538 =N=lambda*R= 0.6818
## Sim. RO-S (rho= 0.67 ) completed. Little law verification: 0.6541 =N=lambda*R= 0.6821
## Sim. SRPT (rho= 0.67 ) completed. Little law verification: 0.6538 =N=lambda*R= 0.6818
## Sim. FCFS (rho= 0.83 ) completed. Little law verification: 0.8923 =N=lambda*R= 0.8712
## Sim. LCFS (rho= 0.83 ) completed. Little law verification: 0.8935 =N=lambda*R= 0.8724
## Sim. RO-S (rho= 0.83 ) completed. Little law verification: 0.8954 =N=lambda*R= 0.8743
## Sim. SRPT (rho= 0.83 ) completed. Little law verification: 0.8876 =N=lambda*R= 0.8667
##
## Response time matrix:
## rho=0.167 rho=0.333 rho=0.5 rho=0.667 rho=0.833
## FCFS 1.013012 1.016820 1.019578 1.023529 1.045497
## LCFS 1.013012 1.016991 1.019023 1.022676 1.046899
## RO-S 1.013012 1.016944 1.020046 1.023162 1.049124
## SRPT 1.013012 1.016820 1.018857 1.022657 1.040010
## Sim. FCFS (rho= 0.17 ) completed. Little law verification: 0.0598 =N=lambda*R= 0.0588
## Sim. LCFS (rho= 0.17 ) completed. Little law verification: 0.0598 =N=lambda*R= 0.0588
## Sim. RO-S (rho= 0.17 ) completed. Little law verification: 0.0598 =N=lambda*R= 0.0588
## Sim. SRPT (rho= 0.17 ) completed. Little law verification: 0.0598 =N=lambda*R= 0.0588
## Sim. FCFS (rho= 0.33 ) completed. Little law verification: 0.125 =N=lambda*R= 0.1264
## Sim. LCFS (rho= 0.33 ) completed. Little law verification: 0.1244 =N=lambda*R= 0.1258
## Sim. RO-S (rho= 0.33 ) completed. Little law verification: 0.1248 =N=lambda*R= 0.1263
## Sim. SRPT (rho= 0.33 ) completed. Little law verification: 0.1242 =N=lambda*R= 0.1257
## Sim. FCFS (rho= 0.5 ) completed. Little law verification: 0.2034 =N=lambda*R= 0.2006
## Sim. LCFS (rho= 0.5 ) completed. Little law verification: 0.2027 =N=lambda*R= 0.2
## Sim. RO-S (rho= 0.5 ) completed. Little law verification: 0.2032 =N=lambda*R= 0.2004
## Sim. SRPT (rho= 0.5 ) completed. Little law verification: 0.2008 =N=lambda*R= 0.1981
## Sim. FCFS (rho= 0.67 ) completed. Little law verification: 0.285 =N=lambda*R= 0.2862
## Sim. LCFS (rho= 0.67 ) completed. Little law verification: 0.2888 =N=lambda*R= 0.29
## Sim. RO-S (rho= 0.67 ) completed. Little law verification: 0.286 =N=lambda*R= 0.2872
## Sim. SRPT (rho= 0.67 ) completed. Little law verification: 0.279 =N=lambda*R= 0.2801
## Sim. FCFS (rho= 0.83 ) completed. Little law verification: 0.3954 =N=lambda*R= 0.3908
## Sim. LCFS (rho= 0.83 ) completed. Little law verification: 0.3947 =N=lambda*R= 0.3901
## Sim. RO-S (rho= 0.83 ) completed. Little law verification: 0.3949 =N=lambda*R= 0.3903
## Sim. SRPT (rho= 0.83 ) completed. Little law verification: 0.3793 =N=lambda*R= 0.3748
##
## Response time matrix:
## rho=0.167 rho=0.333 rho=0.5 rho=0.667 rho=0.833
## FCFS 1.058524 1.137958 1.203478 1.287892 1.406847
## LCFS 1.058634 1.132543 1.199778 1.305192 1.404295
## RO-S 1.058582 1.136691 1.202209 1.292253 1.405021
## SRPT 1.058466 1.131260 1.188422 1.260669 1.349458
We can see that the G/G/k queue (first graph) is in average faster than the G/G/1 queue k times faster (second graph) using the FCFS discipline. That is probably due to the load on the server indeed if the load is too high where in the G/G/1 queue the jobs n will have to wait for the n-1 jobs to finish where with the G/G/k queue a job will have to wait less. So we can think that when the load increases the ratio decreases as the G/G/1 became worse.
###Question 4:
Let’s verify that by ploting the values:
As expected when the load increases the ratio decreases, meaning that the G/G/1 queue becomes worse and worse compared to the G/G/k queue.