Nous avons choisi d’implémenter les algorithmes FIFO / LIFO avec préemption / SRPT.
Note : Le code est relativement générique et issue du TD, à l’exception faite des modifications apportées pour le LIFO, qui inclue la modification du handler d’arrivé.
gen = function(N=100, lambda =.2, mu='uni', policy = 'fifo'){
if(mu != "uni" && mu !="gamma" && mu!="exp"){
print("Loi sup : uniforme,gamma,exp")
}else{
#Global function and init
t = 0;
Arrival = cumsum(rexp(n=N, rate=lambda)); #task arrival
# Task time treatment
if(mu == 'exp'){
Service = rexp(N,rate = 1);
} else if(mu == 'gamma'){
Service = rgamma(N, shape = .1, rate = .1);
} else if(mu == 'uni'){
Service = runif(N, min = 0.5, max = 1.5);
}
#task remaining/task + taks completion
Remaining = rep(NA,N)
Completion = rep(NA,N)
#Current Task
CurrentTask = NA
NextArrival = 1;
#TD's HANDLER CODE
while(T){
dtA = NA;
dtC = NA;
if(length(Arrival[Arrival>t])>0) {
dtA = head(Arrival[Arrival>t],n=1)-t # temps jusqu'à la prochaine arrivée
}
if(!is.na(CurrentTask)) {
dtC = Remaining[CurrentTask]; # temps jusqu'à la prochaine terminaison
}
if(is.na(dtA) & is.na(dtC)) {
break;
}
dt = min(dtA,dtC,na.rm=T)
# Mettre à jour comme il faut:
# la date
t = t + dt;
# les arrivées
if((NextArrival <=N) & (Arrival[NextArrival] <= t)){
if(policy=='lifo_prempt'){#Handling the preamption of the job
Remaining[NextArrival] = Service[NextArrival];
WaitingList=(1:N)[!is.na(Remaining)];
if(length(WaitingList)>0) {
CurrentTask = tail(WaitingList,n=1);
}
NextArrival = NextArrival + 1;
}else{
Remaining[NextArrival] = Service[NextArrival];
NextArrival = NextArrival + 1;
}
}
# le remaining
if(!is.na(CurrentTask)) {
Remaining[CurrentTask] = Remaining[CurrentTask] - dt ;
if(Remaining[CurrentTask] <= 0) {
Completion[CurrentTask] = t;
Remaining[CurrentTask] = NA;
}
CurrentTask = NA;
}
# et currentTask (politique d'ordonnancement: variable)
if(policy == 'fifo'){ #Copied from the TD
WaitingList=(1:N)[!is.na(Remaining)];
if(length(WaitingList)>0) {
CurrentTask = head(WaitingList,n=1);
}
}else if(policy == 'lifo_prempt'){
#We take the last job in the list
WaitingList=(1:N)[!is.na(Remaining)];
if(length(WaitingList)>0) {
CurrentTask = tail(WaitingList,n=1);
}
}else if(policy == 'srpt'){#Same as the TD, just change the selector
WaitingList=(1:N)[!is.na(Remaining)];
if(length(WaitingList)>0) {
CurrentTask = which(Remaining==min(Remaining, na.rm=TRUE))[1];
}
}else{
print("erreur : policy inconnu")
}
}
#Uniformed data frame by Guillaume
return(data.frame(lambda=lambda, mu=mu, Idx=1:N, Completion=Completion, Arrival=Arrival,ModeSched=policy,ModeArri=mu))
}
}
#Source : http://www.cookbook-r.com/Graphs/Plotting_means_and_error_bars_(ggplot2)/
#https://stackoverflow.com/questions/29554796/meaning-of-band-width-in-ggplot-geom-smooth-lm
#https://www.rdocumentation.org/packages/ggplot2/versions/0.9.0/topics/stat_smooth
#We discussed with Quentin about how to render properly on GGplot
stats = function(N=1000, mu='uni', policy = 'fifo'){
lambda = seq(from=0.05, to=.9, by=.1)
X = data.frame();
for(i in lambda){
X = rbind(X,gen(N, mu, lambda=i, policy));
}
library(dplyr);
library(ggplot2);
#Confidence Interval
ci = t.test(X$Completion-X$Arrival)$conf.int[2] - mean(X$Completion-X$Arrival)
X %>% group_by(lambda) %>% summarise(median = mean(Completion-Arrival)) %>%
ggplot(aes(x=lambda, y=median))+geom_line()+geom_smooth(method ='loess', colour = 'purple')+geom_errorbar(aes(ymin=median-ci, ymax=median+ci),width=.05, position=position_dodge(0.05))
}
stats(mu = 'uni', policy='fifo')
## Warning: package 'dplyr' was built under R version 3.4.3
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
## Warning: package 'ggplot2' was built under R version 3.4.3
## Warning: package 'bindrcpp' was built under R version 3.4.3
stats(mu = 'uni', policy='srpt')
stats(mu = 'uni', policy='lifo_prempt')
stats(mu = 'exp', policy='fifo')
stats(mu = 'exp', policy='srpt')
stats(mu = 'exp', policy='lifo_prempt')
stats(mu = 'gamma', policy='fifo')
stats(mu = 'gamma', policy='srpt')
stats(mu = 'gamma', policy='lifo_prempt')
LIFO avec préemption montre des performances très bonne et même meilleure sur des taux d’arrivé faible et donc avec peu de traitement, mais devient rapidement très mauvais quand la quantité de tâches devient important.
FIFO donne des scores relativement bon dans toutes les circosntances malgrès son implémentation rudimentaire.
SRPT montre les meilleurs scores quand le nombre de taches devient important, mais reste moyen en cas de lambda faible. Ceci s’explique par la stratégie des jobs courts avant les longs. Cela améliore le temps de réponse en cas de nombreuses taches en attente car le fait de faire passer en priorité les jobs court donne un certain turnover à l’algorithme.
En observant la distribution avec des lambdas élevé (donc des taches globalements plus longues) :
Dans le cadre d’un temps de traitement uniforme, il n’y a globalement que très peu de diffèrences entre les 3 stratégies (avec quand même un léger retard pour le LIFO avec preempt).
Dans le cadre d’un temps de traitement suivant une loi exponentiel, SRPT et FIFO ont des scores relativement proche (un avantage pour SRPT), mais LIFO avec préemption montre des scores abyssalement mauvais relativement aux deux autres.
Enfin, dans le cadre de temps de traitement régis par une loi gamma, SRPT montre des scores globalement bien meilleurs que FIFO, sans même parler de LIFO (entre 10 à 60 fois mieux)
En observant à lambda faible (peu de tache en entrée) :
Si on n’a pas besoin de se soucier de famine et que le nombre de tâches arrivantes est élevé, alors l’algorithme SRPT est celui à privilégier. " Quant on ne sait pas trop, on prend FIFO :-) " Sinon, en cas d’un faible taux d’arrivé de taches, peu importe l“’algorithme !