Compromis énergétiques de serveurs

set.seed(37);
simul = function(N=100, arrival_rate=20, processing_rate=1, debug=FALSE, cactive=1, cidle=0.5, Servname) {
    Arrival = cumsum(rexp(n=N, rate=arrival_rate));  
    Service = rexp(n=N, rate =processing_rate); # rep(N,x=1); rgamma(N, shape=.1,rate=.1)
    Remaining = rep(N, x=NA);
    Completion = rep(N, x=NA)
    t = 0;
    CurrentTask = NA;
    NextArrival = 1;
    t = 0;
    e = 0; # énergie 
    
    while (TRUE) {
        if(debug) print(t)
        if(debug) print(Arrival);
        if(debug) print(Service);
        if(debug) print(Remaining);
        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)
        
         e = if(!is.na(CurrentTask)) e + cactive * dt else e + cidle * dt;
        
        # Mettre à jour comme il faut:
        #   la date
        t = t + dt;
        #   les arrivées
        if((NextArrival <=N) & (Arrival[NextArrival] <= t)) { ## je met un <= et pas un == car 3-2.9!=0.1 ...
            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: FIFO)
        WaitingList=(1:N)[!is.na(Remaining)];
        if(length(WaitingList)>0) {
            CurrentTask = head(WaitingList,n=1);
        }
    }
    if(debug) print(Completion)
    return(data.frame(
        Arrival_rate=arrival_rate,
        Processing_rate=processing_rate,
        Arrival = Arrival,
        Service = Service,
        Energy = e / t,
        Completion = Completion,
        Servname = Servname
        ))
        
}

Question 1: Comparaison de la performance de S1 et de S2

df_1 = data.frame()
for (r in  seq(from=.1, to=.9, by=.1)) {
    df_1 = rbind(df_1,simul(N=1000, arrival_rate=r, processing_rate=1, debug=FALSE,1,0.5,'1'));
}

df_2 = data.frame()
for (r in  seq(from=.1, to=.9, by=.1)) {
    df_2 = rbind(df_2,simul(N=1000, arrival_rate=r, processing_rate=2, debug=FALSE,4,0.25,'2'));
}

library(dplyr)
df2_1 = df_1 %>% group_by(Arrival_rate, Servname)%>%summarize(Response = mean(Completion - Arrival), Response_se = sd(Completion - Arrival)/sqrt(n()),Consumption = mean(Energy))
df2_2 = df_2 %>% group_by(Arrival_rate, Servname)%>%summarize(Response = mean(Completion - Arrival), Response_se = sd(Completion - Arrival)/sqrt(n()),Consumption = mean(Energy))

df_global = rbind(df2_1,df2_2)

library(ggplot2)
ggplot(df_global, aes(x=Arrival_rate, y=Response, group=Servname)) + geom_line() + ylim(0,10) + geom_line(aes(colour=Servname,group=Servname)) + geom_errorbar(aes(ymin = Response - 2*Response_se, ymax = Response + 2*Response_se))+ ggtitle("Temps de réponse moyen en fonction du taux d'arrivée")

On voit sur ce graphique que l’éspérance du temps de réponse du serveur 2 est bien inferieure à celle du serveur 1. Cette est logique puisque S2 a un meilleur temps de service que S1. Il peux donc traiter les tâches plus rapidement.On remarque également que cette différence s’accentue lorsque le taux d’inter-arrivées augmente.

ggplot(df_global, aes(x=Arrival_rate, y=Consumption, group=Servname)) + geom_line() + ylim(0.25,2.50) + geom_line(aes(colour=Servname,group=Servname)) +ggtitle("Consommation électrique moyenne des serveurs en fonction du taux d'arrivée") 

On cherche maintenant à trouver une estimation à 0.01 près de la valeur de λ pour laquelle S2 devient plus intéressant que S1 sur le plan énergétique.

ggplot(df_global, aes(x=Arrival_rate, y=Consumption, group=Servname)) + geom_line()+ coord_cartesian(xlim = c(.14, .2), ylim = c(.5, .75)) + geom_line(aes(colour=Servname,group=Servname))+ ggtitle("Consommation électrique moyenne des serveurs en fonction du taux d'arrivée")

On observe qu’au delà de λ≈0.17, il est plus rentable d’utiliser S1 que S2.

Question 3

simulS3 = function(N=100, arrival_rate=0.2, debug=FALSE, Servname="Undefined") {
    #Serv1 Parameters
    lambda1 = 1;
    cactive1 = 1;
    cidle1 = .5;

    #Serv2 Parameters
    lambda2 = 2;
    cactive2 = 4;
    cidle2 = .25;

    Arrival = cumsum(rexp(n=N, rate=arrival_rate));  
    Service1 = rexp(n=N, rate =lambda1); # Response time for each server
    Service0 = rexp(n=N, rate =lambda2); # Response time for each server
    Remaining = rep(N, x=NA);
    Queue = rep(N, x=FALSE); # Queue telling what tasks are arrived to be attributed later
    Completion = rep(N, x=NA);
    t = 0;
    e = 0; # énergie 
    CurrentTask = c(NA,NA);
    NextArrival = 1;
    while (TRUE) {
        if(debug) print(t)
        if(debug) print(Queue);
        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[1]) || (!is.na(CurrentTask[2]))) {
            dtC = min(Remaining[CurrentTask[1]],Remaining[CurrentTask[2]],na.rm=T) # temps jusqu'à la prochaine terminaison
        }
        if(is.na(dtA) & is.na(dtC)) {
            break;
        }
        dt = min(dtA,dtC,na.rm=T)
        
        e = if(!is.na(CurrentTask[1])) e + cactive1 * dt else e + cidle1 * dt;
        e = if(!is.na(CurrentTask[2])) e + cactive2 * dt else e + cidle2 * dt;
        
        # Mettre à jour comme il faut:
        #   la date
        t = t + dt;
        #   les arrivées
        if((NextArrival <=N) & (Arrival[NextArrival] <= t)) { ## je met un <= et pas un == car 3-2.9!=0.1 ...
            Queue[NextArrival] = TRUE;
            NextArrival = NextArrival + 1;
        }
        #   le remaining 
        if(!is.na(CurrentTask[1])) {
            Remaining[CurrentTask[1]] = Remaining[CurrentTask[1]] - dt ;
            if(Remaining[CurrentTask[1]] <= 0) {
                Completion[CurrentTask[1]] = t;
                Remaining[CurrentTask[1]] = NA;
                CurrentTask[1] = NA;
            }
        }
        if(!is.na(CurrentTask[2])) {
            Remaining[CurrentTask[2]] = Remaining[CurrentTask[2]] - dt ;
            if(Remaining[CurrentTask[2]] <= 0) {
                Completion[CurrentTask[2]] = t;
                Remaining[CurrentTask[2]] = NA;
                CurrentTask[2] = NA;
            }
        }
        #   et currentTask (politique d'ordonnancement: FIFO)
        WaitingList=(1:N)[Queue];
        if(length(WaitingList)>0) {
            Next = head(WaitingList,n=1);
            if(is.na(CurrentTask[1])){
                CurrentTask[1] = Next;
                Remaining[Next] = Service1[Next]
                Queue[Next] = FALSE;
            }
        }
    }
    if(debug) print(Completion)
    return(data.frame(
        Arrival_rate=arrival_rate,
        Processing_rate=1,
        Arrival = Arrival,
        Service = 1,
        Energy = e / t,
        Completion = Completion,
        Servname = Servname
        ))
        
}

df_1 = data.frame()
for (r in  seq(from=.1, to=1.5, by=.1)) {
    df_1 = rbind(df_1,simul(N=1000, arrival_rate=r, processing_rate=1, debug=FALSE,1,0.5,'1'));
}

df_2 = data.frame()
for (r in  seq(from=.1, to=1.5, by=.1)) {
    df_2 = rbind(df_2,simul(N=1000, arrival_rate=r, processing_rate=2, debug=FALSE,4,0.25,'2'));
}

df_3 = data.frame()
for (r in  seq(from=.1, to=1.5, by=.1)) {
    df_3 = rbind(df_3,simulS3(N=1000, arrival_rate=r, Servname = '3'));
}

df2_1 = df_1 %>% group_by(Arrival_rate, Servname)%>%summarize(Response = mean(Completion - Arrival), Response_se = sd(Completion - Arrival)/sqrt(n()),Consumption = mean(Energy))
df2_2 = df_2 %>% group_by(Arrival_rate, Servname)%>%summarize(Response = mean(Completion - Arrival), Response_se = sd(Completion - Arrival)/sqrt(n()),Consumption = mean(Energy))
df2_3 = df_3 %>% group_by(Arrival_rate, Servname)%>%summarize(Response = mean(Completion - Arrival), Response_se = sd(Completion - Arrival)/sqrt(n()),Consumption = mean(Energy))

df_global = rbind(df2_1,df2_2)

df_global = rbind(df_global,df2_3)


ggplot(df_global, aes(x=Arrival_rate, y=Consumption, group=Servname)) + geom_line() + ylim(0.25,2.50) + geom_line(aes(colour=Servname,group=Servname)) +ggtitle("Temps de réponse moyen en fonction du taux d'arrivée") 

Pour des taux d’inter-arrivées faibles, le serveur S3 a un taux de réponse proche de celui de S1, S1 peut dans ce cas gérer presque toutes les tâches et le serveur S2 est donc rarement utilisé.

Lorsque les taux d’inter-arrivées augmentent, le temps de réponse du serveur S3 se rapproche de celui de S2, car le serveur S2 plus de tâche.On constate que le temps de réponse de S3 est meilleur que celui de S2 pour un taux d’inter-arrivées de 0,9. Ce qui est normal puisque c’est une situation dans laquelle il y a beaucoup de tâches dans la file d’attente. Cette attente est donc réduite par le serveur S1.

ggplot(df_global, aes(x=Arrival_rate, y=Consumption, group=Servname)) + geom_line() + ylim(0.25,2.50) + geom_line(aes(colour=Servname,group=Servname)) +ggtitle("Consommation électrique moyenne des serveurs en fonction du taux d'arrivée") 

On voit que les courbes de S2 et S3 ne se croisent pas. Il est cependant plus rentable d’utiliser le serveur S2 par rapport au serveur S3 d’un point de vue uniquement energetique.