Aller au contenu

Analysez un réseau fermé avec MVA

Au chapitre 3, les bornes asymptotiques nous ont appris qu’avec un disque SSD, les 400 utilisateurs attendus au pic de la billetterie restaient sous le point de saturation. Mais une borne est optimiste : elle suppose qu’aucune requête n’attend jamais. Le vrai temps de réponse sera-t-il de 0,1 s, de 0,3 s ou de 2 s ? Le chef de projet veut un chiffre.

Pour l’obtenir, il faut tenir compte des files d’attente à chaque ressource, dans un système où le nombre d’utilisateurs est fixé. C’est exactement ce que fait l’analyse par valeurs moyennes (Mean Value Analysis, MVA), un algorithme d’une dizaine de lignes qui donne des résultats exacts.

Au chapitre 4, nous avons traité la billetterie comme un réseau ouvert : les requêtes arrivent de l’extérieur à un taux λ\lambda fixé, quel que soit l’état du système. Ce n’est pas tout à fait réaliste pour un site interactif. Quand le site ralentit, les utilisateurs attendent leur réponse avant de cliquer à nouveau : ils envoient donc moins de requêtes. Le système se régule tout seul.

Un réseau fermé capture ce comportement. On fixe le nombre NN d’utilisateurs, qui tournent en boucle :

  • ils réfléchissent pendant un temps moyen ZZ (un centre de délai, sans file) ;
  • ils envoient une requête, qui visite les ressources (des files d’attente, avec leurs demandes DiD_i) ;
  • ils reçoivent la réponse, et recommencent.

Le débit XX n’est plus une donnée : c’est un résultat, qui dépend de NN.

Réseau ouvertRéseau fermé
Donnée d’entréetaux d’arrivée λ\lambdanombre de clients NN, temps de réflexion ZZ
DébitX=λX = \lambda (imposé)calculé, X(N)X(N)
Saturationla file grandit sans limite si λ≥1/Dmax⁡\lambda \geq 1/D_{\max}le débit plafonne à 1/Dmax⁡1/D_{\max}, le temps de réponse grandit
ExemplesAPI publique, capteurssite interactif, traitement par lots, pool de connexions
réseau ouvertλ imposésortieX = λ, quel que soitl’état du systèmeréseau ferméN utilisateursréfléchissent (Z)requêteréponseX(N) est un résultat :si le site ralentit, on clique moinswebprocesseurdisquewebprocesseurdisque
Dans le réseau ouvert, le débit est imposé de l'extérieur ; dans le réseau fermé, les N utilisateurs attendent leur réponse avant de recliquer, et le débit devient un résultat du modèle.

Au chapitre 4, nous avons raisonné ainsi pour une file M/M/1 : un client qui arrive trouve en moyenne QQ clients devant lui, et son temps de réponse vaut S(1+Q)S(1 + Q). Dans un réseau fermé, il y a une subtilité. Le client qui arrive sur une ressource ne peut pas se voir lui-même dans la file ! Le théorème d’arrivée (arrival theorem) précise ce qu’il voit : dans un réseau fermé à nn clients (de forme produit, voir plus bas), un client qui arrive sur une ressource voit en moyenne l’état du réseau à n−1n - 1 clients.

C’est ce qui rend possible une récurrence : si l’on connaît les longueurs de file moyennes avec n−1n - 1 clients, on en déduit tout avec nn clients. On part de Qi(0)=0Q_i(0) = 0 (réseau vide) et on ajoute les clients un par un.

Pour n=1,2,…,Nn = 1, 2, \dots, N, on applique successivement trois relations.

1. Temps de résidence sur chaque ressource (théorème d’arrivée) :

Ri(n)=Di (1+Qi(n−1))R_i(n) = D_i \,\big(1 + Q_i(n - 1)\big)

où Ri(n)R_i(n) est le temps total qu’une requête passe sur la ressource ii (attente et service, toutes visites comprises), DiD_i sa demande de service et Qi(n−1)Q_i(n - 1) la longueur moyenne de sa file avec un client de moins. Pour un centre de délai, il n’y a pas d’attente : Ri(n)=DiR_i(n) = D_i.

2. Débit du système (Little sur la boucle complète) :

X(n)=nZ+∑iRi(n)X(n) = \frac{n}{Z + \sum_i R_i(n)}

3. Longueur de chaque file (Little sur chaque ressource) :

Qi(n)=X(n) Ri(n)Q_i(n) = X(n) \, R_i(n)

Le temps de réponse du système est R(n)=∑iRi(n)R(n) = \sum_i R_i(n), et les utilisations se déduisent par Ui=X(n) DiU_i = X(n) \, D_i.

1. théorème d’arrivée2. Little sur la boucle3. Little sur chaque ressourcen ← n + 1départ:(0)=0Qin = 1 : R = 0,110 s, X = 0,06618 req/sn = 2 : R = 0,110324 s, X = 0,13236 req/sdisque : Q(1) ≈ 0,00397 puisR(2) = 0,060 × 1,00397 ≈ 0,060238 sRnDQniii()=(1+(−1))XnZnRn()=+()∑iiQnXnRnii()=()()
Une itération de MVA : les files avec n − 1 clients donnent les temps de résidence, puis le débit, puis les nouvelles files. Au centre, les chiffres de la billetterie pour n = 1 et n = 2.

En Python, l’algorithme tient en quelques lignes :

def mva(D, Z, N):
"""MVA exacte, une classe de clients. D : demandes de service (s), Z : temps de réflexion (s)."""
Q = [0.0] * len(D) # files vides au départ
for n in range(1, N + 1):
R = [d * (1 + q) for d, q in zip(D, Q)] # 1. théorème d'arrivée
X = n / (Z + sum(R)) # 2. Little sur la boucle
Q = [X * r for r in R] # 3. Little sur chaque ressource
return X, sum(R), R
D = [0.020, 0.030, 0.060] # web, processeur, disque
for N in (50, 100, 152, 200, 250, 300, 400):
X, R, _ = mva(D, Z=15, N=N)
print(f"N = {N:3d} : X = {X:5.2f} req/s, R = {R:5.3f} s")

Le coût est de l’ordre de N×KN \times K opérations pour KK ressources : quelques microsecondes pour des centaines d’utilisateurs.

Comparons les résultats exacts aux bornes du chapitre 3 :

NNBorne sur XX (req/s)XX exact (MVA)Borne sur RR (s)RR exact (MVA)UdisqueU_{\text{disque}}
503,313,300,110,1290,20
15210,069,990,110,2140,60
20013,2413,040,110,3340,78
25016,5515,800,110,8270,95
30016,6716,663,003,0051,00
40016,6716,679,009,0001,00

Plusieurs choses sautent aux yeux :

  • Avec 152 utilisateurs, MVA prédit X≈9,99X \approx 9{,}99 req/s et R≈0,21R \approx 0{,}21 s. C’est exactement ce que l’on avait mesuré l’an dernier au chapitre 2 (10 req/s et 0,2 s). Le modèle est validé par la mesure : on peut lui faire confiance pour extrapoler.
  • Loin de la saturation (50 utilisateurs) ou très au-delà (400), les bornes sont presque exactes. C’est autour de N∗≈252N^* \approx 252 que l’écart est le plus grand : à 250 utilisateurs, la borne promet 0,11 s, la réalité est 0,83 s.
  • Presque tout le temps de réponse se passe sur le disque : à 200 utilisateurs, 0,258 s sur les 0,334 s.
050100150200250300nombre d’utilisateurs N0,00,20,40,60,81,01,2temps de réponse (s)N* ≈ 252152 : 0,214 s(0,2 s mesurés)200 : 0,334 sdont 0,258 s de disque250 : 0,83 s(borne : 0,11 s)borne du chapitre 3R exact (MVA)disqueprocesseurweb
La tranche du disque (magenta) fait presque tout le temps de réponse. À 152 utilisateurs, le modèle retrouve les 0,2 s mesurés ; à 250, la borne promet 0,11 s quand la réalité est 0,83 s.

Et avec le SSD (Ddisque=30D_{\text{disque}} = 30 ms) ? Pour 400 utilisateurs, MVA donne X≈26,1X \approx 26{,}1 req/s et R≈0,31R \approx 0{,}31 s, avec le processeur et le disque à 78 % d’utilisation. Voilà le chiffre que voulait le chef de projet : l’objectif est tenable, mais avec peu de marge.

0200400600nombre d’utilisateurs N05101520253035débit X (req/s)16,7 req/s26,1 req/sdisque actuel (60 ms)SSD (30 ms)0200400600nombre d’utilisateurs N0,10,3131030temps de réponse R (s, échelle log)9,00 s0,31 s
Avec le SSD, les 400 utilisateurs obtiennent 26,1 req/s et 0,31 s, au lieu de 9 s avec le disque actuel. Mais la courbe du SSD commence déjà à se redresser : la marge est faible.

MVA est exacte pour les réseaux dits à forme produit (théorème BCMP), dont les conditions principales sont :

  • une seule classe de clients (tous les utilisateurs ont les mêmes demandes) ;
  • des ressources indépendantes de la charge (load independent) : le temps de service ne dépend pas du nombre de clients présents ;
  • des disciplines compatibles : FCFS avec services exponentiels de même moyenne pour tous, partage de processeur, LCFS avec préemption, ou centre de délai.

Dans la réalité, ces hypothèses sont souvent un peu fausses : plusieurs types de requêtes (consultation et paiement), des verrous en base de données, des serveurs multicœurs. On dispose alors d’extensions (MVA multiclasse, centres dépendants de la charge) et d’approximations. La plus connue, l’approximation de Schweitzer, évite la récurrence en supposant Qi(n−1)≈n−1nQi(n)Q_i(n - 1) \approx \frac{n - 1}{n} Q_i(n) et en itérant jusqu’à convergence. Pour la billetterie à 200 utilisateurs, elle donne 13,03 req/s et 0,346 s, contre 13,04 req/s et 0,334 s pour MVA exacte.

  • Un réseau fermé fixe le nombre d’utilisateurs NN et leur temps de réflexion ZZ ; le débit devient un résultat, qui plafonne à 1/Dmax⁡1/D_{\max}.
  • Le théorème d’arrivée : un client qui arrive voit le réseau tel qu’il est avec n−1n - 1 clients.
  • MVA : Ri(n)=Di(1+Qi(n−1))R_i(n) = D_i(1 + Q_i(n - 1)), X(n)=n/(Z+∑iRi(n))X(n) = n/(Z + \sum_i R_i(n)), Qi(n)=X(n)Ri(n)Q_i(n) = X(n) R_i(n), en partant de Qi(0)=0Q_i(0) = 0. Coût en O(NK)O(NK).
  • Les résultats de MVA se placent sous les bornes asymptotiques, et s’en écartent le plus autour du point de saturation N∗N^*.
  • MVA est exacte pour les réseaux à forme produit (une classe, ressources indépendantes de la charge) ; sinon, on utilise des extensions ou des approximations comme celle de Schweitzer.
  • Un modèle se calibre et se valide sur des mesures avant de servir à extrapoler.

Exercice · Dérouler MVA à la main

facile

Un système de traitement par lots (sans temps de réflexion, Z=0Z = 0) a deux ressources : un processeur de demande 0,2 s et un disque de demande 0,1 s. Déroulez MVA pour N=1,2,3N = 1, 2, 3 et donnez à chaque fois le débit et le temps de réponse.

Voir la solution

N=1N = 1 : RCPU=0,2R_{\text{CPU}} = 0{,}2, Rdisque=0,1R_{\text{disque}} = 0{,}1, R=0,3R = 0{,}3 s, X=1/0,3≈3,33X = 1/0{,}3 \approx 3{,}33 lots/s. QCPU=3,33×0,2≈0,667Q_{\text{CPU}} = 3{,}33 \times 0{,}2 \approx 0{,}667, Qdisque≈0,333Q_{\text{disque}} \approx 0{,}333.

N=2N = 2 : RCPU=0,2×1,667≈0,333R_{\text{CPU}} = 0{,}2 \times 1{,}667 \approx 0{,}333, Rdisque=0,1×1,333≈0,133R_{\text{disque}} = 0{,}1 \times 1{,}333 \approx 0{,}133, R≈0,467R \approx 0{,}467 s, X=2/0,467≈4,29X = 2/0{,}467 \approx 4{,}29 lots/s. QCPU≈1,429Q_{\text{CPU}} \approx 1{,}429, Qdisque≈0,571Q_{\text{disque}} \approx 0{,}571.

N=3N = 3 : RCPU=0,2×2,429≈0,486R_{\text{CPU}} = 0{,}2 \times 2{,}429 \approx 0{,}486, Rdisque=0,1×1,571≈0,157R_{\text{disque}} = 0{,}1 \times 1{,}571 \approx 0{,}157, R≈0,643R \approx 0{,}643 s, X=3/0,643≈4,67X = 3/0{,}643 \approx 4{,}67 lots/s.

Le débit tend vers 1/Dmax⁡=51/D_{\max} = 5 lots/s : le processeur est le goulot.

En code

D, Z = [0.2, 0.1], 0 # processeur, disque (s) ; pas de temps de réflexion
Q = [0.0, 0.0] # files vides au départ
for n in range(1, 4):
R = [d * (1 + q) for d, q in zip(D, Q)] # théorème d'arrivée
X = n / (Z + sum(R)) # Little sur la boucle
Q = [X * r for r in R] # Little sur chaque ressource
print(f"N = {n}: R = {sum(R):.3f} s, X = {X:.2f} lots/s, Q = {[round(q, 3) for q in Q]}")
# N = 1: R = 0.300 s, X = 3.33 lots/s, Q = [0.667, 0.333]
# N = 2: R = 0.467 s, X = 4.29 lots/s, Q = [1.429, 0.571]
# N = 3: R = 0.643 s, X = 4.67 lots/s, Q = [2.267, 0.733]

Exercice · Borne ou valeur exacte ?

moyen

Pour le système de l’exercice précédent, calculez les bornes asymptotiques sur XX et RR pour N=1,2,3N = 1, 2, 3 et comparez aux valeurs exactes. Pour quel NN l’écart relatif sur le débit est-il le plus grand ?

Voir la solution

D=0,3D = 0{,}3 s, Dmax⁡=0,2D_{\max} = 0{,}2 s, Z=0Z = 0, donc N∗=0,3/0,2=1,5N^* = 0{,}3/0{,}2 = 1{,}5.

NNBorne XXXX exactBorne RRRR exact
1min⁡(3,33 ; 5)=3,33\min(3{,}33 \, ; \, 5) = 3{,}333,33max⁡(0,3 ; 0,2)=0,3\max(0{,}3 \, ; \, 0{,}2) = 0{,}30,3
2min⁡(6,67 ; 5)=5\min(6{,}67 \, ; \, 5) = 54,29max⁡(0,3 ; 0,4)=0,4\max(0{,}3 \, ; \, 0{,}4) = 0{,}40,467
3min⁡(10 ; 5)=5\min(10 \, ; \, 5) = 54,67max⁡(0,3 ; 0,6)=0,6\max(0{,}3 \, ; \, 0{,}6) = 0{,}60,643

Pour N=1N = 1, la borne est exacte (un client seul n’attend jamais). L’écart relatif sur le débit est le plus grand pour N=2N = 2 (5 contre 4,29, soit environ 17 %), c’est-à-dire juste après le point de saturation N∗=1,5N^* = 1{,}5. Ensuite, l’écart se réduit à mesure que le processeur approche 100 % d’utilisation.

En code

def mva(D, Z, N):
Q = [0.0] * len(D)
for n in range(1, N + 1):
R = [d * (1 + q) for d, q in zip(D, Q)]
X = n / (Z + sum(R))
Q = [X * r for r in R]
return X, sum(R)
D, Z = [0.2, 0.1], 0
for N in (1, 2, 3):
X, R = mva(D, Z, N)
X_borne = min(N / (sum(D) + Z), 1 / max(D))
R_borne = max(sum(D), N * max(D) - Z)
ecart = (X_borne - X) / X # écart relatif sur le débit
print(f"N = {N}: X {X_borne:.2f} / {X:.2f} R {R_borne:.2f} / {R:.3f} écart {ecart:.1%}")
# N = 1: X 3.33 / 3.33 R 0.30 / 0.300 écart 0.0%
# N = 2: X 5.00 / 4.29 R 0.40 / 0.467 écart 16.7%
# N = 3: X 5.00 / 4.67 R 0.60 / 0.643 écart 7.1%

Exercice · Choisir l'amélioration avec MVA

difficile

Reprenez la billetterie à 200 utilisateurs (Z=15Z = 15 s, demandes 20, 30 et 60 ms). En réutilisant la fonction mva du chapitre, comparez le temps de réponse obtenu avec :

  1. un processeur deux fois plus rapide ;
  2. un disque deux fois plus rapide.

Sans calculer, laquelle des deux options réduit le plus le temps de réponse ? Vérifiez ensuite.

Voir la solution

Intuitivement, c’est le disque : c’est le goulot, et il concentre l’essentiel du temps de réponse (0,258 s sur 0,334 s à 200 utilisateurs).

for nom, D in [("CPU x2", [0.020, 0.015, 0.060]), ("disque x2", [0.020, 0.030, 0.030])]:
X, R, _ = mva(D, Z=15, N=200)
print(f"{nom:<10} X = {X:.2f} req/s, R = {R:.3f} s")

On obtient environ 0,305 s avec le processeur deux fois plus rapide, contre 0,334 s au départ : un gain de 9 % seulement. Avec le disque deux fois plus rapide, on obtient environ 0,126 s : le temps de réponse est divisé par plus de 2,6. C’est la confirmation, cette fois avec les files d’attente, de la règle du chapitre 3 : on améliore d’abord le goulot.

En code

def mva(D, Z, N):
"""MVA exacte à une classe : renvoie le débit et le temps de réponse."""
Q = [0.0] * len(D)
for n in range(1, N + 1):
R = [d * (1 + q) for d, q in zip(D, Q)]
X = n / (Z + sum(R))
Q = [X * r for r in R]
return X, sum(R)
options = {"départ": [0.020, 0.030, 0.060], # web, processeur, disque (s)
"CPU x2": [0.020, 0.015, 0.060],
"disque x2": [0.020, 0.030, 0.030]}
for nom, D in options.items():
X, R = mva(D, Z=15, N=200)
print(f"{nom:<10} X = {X:.2f} req/s, R = {R:.3f} s")
# départ X = 13.04 req/s, R = 0.334 s
# CPU x2 X = 13.07 req/s, R = 0.305 s
# disque x2 X = 13.22 req/s, R = 0.126 s