Read in English · Leer en español · Auf Deutsch lesen
Section 2.4 sur 2.8 · Intermédiaire · 24 min de lecture
Performances et scalabilité : latence, débit, percentiles et charge
Leçon vidéo
5 min · narré en anglais, avec sous-titresÉcouter seulement (audio)
Lire la transcription
Les performances sont la qualité que l’on cite en premier et que l’on spécifie le plus mal. Dans cette leçon, vous allez découvrir la poignée d’idées et de formules qu’utilisent les professionnels pour bien les spécifier.
Imaginez un supermarché. Le temps que vous passez à la caisse, c’est la latence : le temps que prend une unité de travail. Le nombre de clients que les caisses servent en une heure, c’est le débit : la quantité de travail accomplie par unité de temps. Une bonne exigence énonce les deux, avec la charge à laquelle ils s’appliquent. Votre temps à la caisse se compose en partie du passage des articles, le travail lui-même, et en partie de la file devant vous. Quand les caisses ne sont occupées qu’à moitié, vous n’attendez presque pas. Quand elles sont presque toujours occupées, les files s’allongent très vite, même si chaque passage d’article prend le même temps.
Le logiciel fonctionne de la même façon. Le temps de réponse que voit un client, c’est tout ce qui se passe entre l’envoi d’une requête et la réception de la réponse. Une partie de ce temps est passée à attendre dans une file, parce que le worker est occupé par la requête de quelqu’un d’autre. Une autre partie est le temps de service : le travail lui-même. Le temps réseau s’ajoute dans les deux sens. Les deux parties appellent des remèdes différents. Une requête en base plus rapide réduit le temps de service. Seules plus de capacité ou moins de charge réduisent le temps d’attente, et sous charge, c’est généralement l’attente qui domine. Une requête peut prendre cinq millisecondes, mais en attendre trois cents pour obtenir une connexion libre.
Voici pourquoi les moyennes trompent. Un service de recherche enregistre mille temps de réponse. Neuf cents prennent quatre-vingts millisecondes, quatre-vingts en prennent cent cinquante, quinze en prennent mille deux cents, et cinq en prennent quatre mille. La moyenne est de cent vingt-deux millisecondes, ce qui semble correct. Les professionnels utilisent plutôt des percentiles. Le cinquantième percentile, ou médiane, est la valeur que bat la moitié des requêtes. Ici, c’est quatre-vingts millisecondes. Le quatre-vingt-quinzième percentile, p quatre-vingt-quinze, est la valeur que battent quatre-vingt-quinze pour cent des requêtes : cent cinquante millisecondes. Mais le p quatre-vingt-dix-neuf est de mille deux cents millisecondes. Et le p quatre-vingt-dix-neuf virgule neuf est de quatre mille millisecondes. Donc deux pour cent des utilisateurs attendent plus d’une seconde. À trois cents recherches par seconde, cela fait six personnes chaque seconde. Une exigence rédigée sous forme de moyenne validerait ce système. Une exigence rédigée en percentiles le fait échouer, à juste titre.
L’extrémité lente de la distribution s’appelle la queue. Un utilisateur qui charge quarante ressources pour voir une page attend la plus lente de ces quarante, pas la moyenne. Ainsi, un événement rare à un pour cent touche environ un tiers des affichages de pages. Dans les grands systèmes, une requête peut se diffuser en éventail vers cent serveurs et les attendre tous. Si chacun est lent une fois sur cent, la requête de l’utilisateur est lente environ soixante-trois pour cent du temps.
La loi de Little est la formule la plus utile en planification de la capacité. Le nombre de requêtes en cours est égal au taux d’arrivée multiplié par le temps que chacune passe à l’intérieur. Mettons-la en pratique. Le service des trajets d’une application de transport avec chauffeur traite mille huit cents requêtes par seconde au pic du soir. Chaque requête occupe une connexion à la base de données pendant quarante millisecondes. Donc, en moyenne, mille huit cents fois zéro virgule zéro quatre, soit soixante-douze connexions, sont occupées en même temps. Mais six instances de dix connexions chacune n’en offrent que soixante. Les requêtes feront la queue pour obtenir une connexion, et les temps de réponse grimperont jusqu’à ce que les délais d’expiration se déclenchent. Pour que le pool reste occupé à environ soixante-dix pour cent au plus, il vous faut soixante-douze divisé par zéro virgule sept, soit environ cent trois connexions, ou environ dix-huit par instance.
Pourquoi garder cette marge ? Cette courbe montre une file à un seul serveur avec un temps de service de vingt millisecondes. Jusqu’à cinquante pour cent d’occupation, le temps de réponse moyen n’est que de quarante millisecondes. À quatre-vingts pour cent d’occupation, il est de cent millisecondes. À quatre-vingt-dix pour cent, il double pour atteindre deux cents. Au-delà, il monte presque à la verticale. Plat, puis un coude, puis une falaise. Ne prévoyez donc jamais de faire tourner des ressources critiques près de leur pleine capacité.
La scalabilité est la capacité à absorber plus de charge en ajoutant des ressources. La scalabilité verticale donne plus de puissance à une machine. C’est simple, mais cela se heurte à des limites strictes et à des prix croissants. La scalabilité horizontale ajoute des machines. Il n’y a pas de plafond fixe, mais le logiciel doit être conçu pour cela. Et le parallélisme a ses limites. La loi d’Amdahl dit que si dix pour cent du travail doit se faire une étape après l’autre, aucun nombre de processeurs ne peut rendre la tâche plus de dix fois plus rapide. Pire, maintenir la cohérence des données entre les nœuds a un coût qui croît rapidement, si bien qu’au-delà d’un certain point, ajouter des nœuds réduit en fait la capacité. Cherchez les parties séquentielles cachées, comme un verrou unique ou un coordinateur central.
La section complète détaille la loi d’Amdahl et la loi universelle de scalabilité, et présente les principales tactiques de performances. Ensuite, dans la section deux point cinq, nous verrons la disponibilité et la fiabilité. À très vite.
Ce qu’il faut savoir
- La latence est le temps que prend une unité de travail ; le débit est la quantité de travail accomplie par unité de temps. Une exigence de performances devrait énoncer les deux, avec la charge à laquelle ils s’appliquent.
- Le temps de réponse est le temps de service plus le temps d’attente (plus le temps réseau) ; sous charge, c’est généralement le temps d’attente qui domine.
- Spécifiez et surveillez des percentiles comme p95 et p99, pas des moyennes ; dans les systèmes en fan-out, une réponse lente rare sur un serveur devient courante pour les utilisateurs (Dean et Barroso, 2013).
- La loi de Little, L = λ × W, relie le débit de requêtes, le temps de réponse et la concurrence ; elle sert de base au dimensionnement des pools et des limites.
- Dans une file à un seul serveur, le temps de réponse moyen vaut S ÷ (1 − ρ) ; il double donc entre 80 % et 90 % de taux d’utilisation : gardez les ressources critiques bien en dessous de la saturation.
- La loi d’Amdahl limite l’accélération à 1 ÷ s pour une fraction séquentielle s ; la loi universelle de scalabilité ajoute des coûts de cohérence qui font culminer puis décroître la capacité.
Deux questions différentes : à quelle vitesse, et combien
Les performances sont la qualité que l’on cite en premier et que l’on spécifie le plus mal. Elles recouvrent deux questions différentes. La latence demande combien de temps prend une unité de travail : combien de millisecondes s’écoulent entre le moment où l’on touche « Payer » et celui où l’on voit « Paiement accepté ». Le débit demande quelle quantité de travail le système accomplit par unité de temps : 1200 paiements par seconde, 40 000 commandes par heure. Les deux sont liés mais pas interchangeables. Un autocar qui transporte 50 personnes de Londres à Oxford en 100 minutes a une moins bonne latence et un bien meilleur débit qu’une voiture de sport qui transporte deux personnes en 60 minutes.
Dans le logiciel, la même tension apparaît partout. Regrouper les écritures en base de données par lots améliore le débit, mais fait attendre chaque écriture individuelle jusqu’à son lot. Ajouter un cache améliore les deux pour les lectures répétées, mais ne fait rien pour la première. Une bonne exigence de performances précise donc la question qu’elle traite, et énonce généralement les deux : « à 1200 paiements par seconde (débit), 99 % aboutissent en moins de 800 ms (latence) ». L’ISO/IEC 25010 les range sous l’efficacité des performances, en tant que comportement temporel, utilisation des ressources et capacité (section 2.2).
Temps de réponse = temps de service + temps d’attente
Quand vous mesurez la latence du côté du client, vous obtenez le temps de réponse : tout ce qui s’est passé entre l’envoi de la requête et la réception de la réponse. Il est utile de le décomposer. Le temps de service est le temps qu’une ressource passe réellement à travailler sur votre requête : l’analyser, exécuter la requête en base, produire le résultat. Le temps d’attente est le temps que votre requête passe dans une file parce que la ressource est occupée par celle de quelqu’un d’autre. Le temps de transfert réseau s’ajoute par-dessus, dans les deux sens.
La distinction compte, car les deux parties réagissent à des remèdes différents. Le temps de service diminue quand vous rendez le travail lui-même moins coûteux : un meilleur index, une charge utile plus petite, un algorithme plus rapide. Le temps d’attente diminue quand vous ajoutez de la capacité ou réduisez la charge, et il est souvent bien plus long que le temps de service quand un système est chargé. Bien des enquêtes sur une « base de données lente » se terminent par la découverte que chaque requête prend 5 ms mais attend 300 ms une connexion libre dans le pool.
Où passe le temps
Percentiles : pourquoi les moyennes trompent
Les temps de réponse ne se résument jamais à un seul nombre ; ils forment une distribution, et celle-ci est presque toujours asymétrique. La plupart des requêtes sont rapides, et une minorité est lente parce qu’elle tombe sur un cache froid, une pause du ramasse-miettes, un verrou, une nouvelle tentative ou un voisin surchargé. La moyenne arithmétique mélange les deux groupes en un chiffre qui ne décrit personne. Les professionnels utilisent plutôt des percentiles. Le 50e percentile (p50, la médiane) est la valeur que bat la moitié des requêtes ; p95 est la valeur que battent 95 % des requêtes ; p99 et p99.9 décrivent les 1 % et 0,1 % les plus lents.
L’extrémité lente de la distribution s’appelle la queue (tail), et elle compte plus qu’il n’y paraît. Les utilisateurs situés dans la queue sont souvent vos meilleurs clients : ceux qui ont les plus gros paniers, les historiques les plus longs et le plus de données. Un utilisateur qui charge 40 ressources pour afficher une page subit la plus lente de ces 40, pas la moyenne. Et la probabilité qu’une page de 40 ressources tombe sur au moins une requête au niveau du p99 est de 1 − 0,9940 ≈ 33 % : un événement « rare » à 1 % touche un tiers des affichages de pages.
Exemple commenté
Exemple commenté : la moyenne qui cache le problème
Une API de recherche de produits enregistre 1000 temps de réponse en une minute : 900 à 80 ms, 80 à 150 ms, 15 à 1200 ms (un cache froid) et 5 à 4000 ms (un délai d’expiration et une nouvelle tentative).
| Somme de tous les temps | 900 × 80 + 80 × 150 + 15 × 1200 + 5 × 4000 = 72 000 + 12 000 + 18 000 + 20 000 = 122 000 ms |
|---|---|
| Moyenne | 122 000 ÷ 1000 = 122 ms. Semble satisfaisant au regard d’un objectif de 200 ms. |
| p50 (médiane) | La 500e requête la plus rapide : 80 ms |
| p95 | La 950e requête la plus rapide se trouve dans le groupe à 150 ms (requêtes 901 à 980) : 150 ms |
| p99 | La 990e se trouve dans le groupe à 1200 ms (requêtes 981 à 995) : 1200 ms |
| p99.9 | La 999e se trouve dans le groupe à 4000 ms (996 à 1000) : 4000 ms |
| Qui est touché | 20 requêtes sur 1000 (2 %) ont pris plus d’une seconde. À 300 recherches par seconde, cela fait 6 utilisateurs par seconde, soit environ 21 600 par heure. |
La moyenne de 122 ms respecte un objectif de 200 ms alors que 2 % des utilisateurs attendent plus d’une seconde. Un scénario rédigé sous la forme « moyenne inférieure à 200 ms » validerait ce système.
Rédiger l’exigence sous la forme « p95 ≤ 200 ms et p99 ≤ 500 ms » le fait échouer, à juste titre, et oriente l’équipe vers les chemins du cache froid et des nouvelles tentatives.
Les percentiles ne se moyennent pas. Le p99 sur dix serveurs n’est pas la moyenne de leurs dix valeurs de p99 ; il doit être calculé à partir des données combinées, ou estimé à partir d’histogrammes fusionnés.
# Why the mean misleads: 1,000 response times from one minute of traffic
import statistics
times_ms = [80] * 900 + [150] * 80 + [1200] * 15 + [4000] * 5
mean = statistics.mean(times_ms)
cuts = statistics.quantiles(times_ms, n=1000, method="inclusive")
p50, p95, p99, p999 = cuts[499], cuts[949], cuts[989], cuts[998]
print(f"mean {mean:.0f} ms, p50 {p50:.0f}, p95 {p95:.0f}, p99 {p99:.0f}, p99.9 {p999:.0f}")
# mean 122 ms, p50 80, p95 150, p99 1200, p99.9 4000La latence de queue à grande échelle
La queue devient l’événement principal dans les grands systèmes qui recourent au fan-out (diffusion en éventail), où une requête d’utilisateur est divisée en de nombreuses requêtes parallèles vers d’autres serveurs et où la réponse doit les attendre toutes. Un moteur de recherche, un fil d’actualité social ou une page de recommandations peut consulter des dizaines ou des centaines de shards back-end pour chaque requête. Jeffrey Dean et Luiz André Barroso, de Google, ont analysé ce phénomène dans The Tail at Scale (Communications of the ACM, 2013).
Leur illustration est simple. Supposons que chaque serveur réponde généralement en 10 ms, mais mette 1 seconde pour une requête sur 100. Une requête d’utilisateur qui attend un seul serveur est lente 1 % du temps. Une requête d’utilisateur qui attend 100 serveurs de ce type est lente dès que l’un d’eux est lent : 1 − 0,99100 ≈ 63 % du temps. Un événement rare sur un serveur devient le cas courant pour l’utilisateur. Dean et Barroso rapportent des mesures d’un service Google dans lequel la latence au 99e percentile pour une seule requête feuille était de 10 ms, mais le 99e percentile de l’attente de toutes les requêtes feuilles était de 140 ms.
Comme on ne peut pas éliminer toutes les sources de variabilité (matériel partagé, tâches d’arrière-plan, ramasse-miettes, files réseau), ils plaident pour des conceptions tolérantes à la queue, de même que les conceptions tolérantes aux pannes acceptent que le matériel tombe en panne. Leurs techniques comprennent les requêtes de couverture (hedged requests : envoyer une deuxième copie à une autre réplique si la première n’a pas répondu dans, disons, le temps du 95e percentile, et utiliser celle qui revient en premier), les requêtes liées (tied requests) qui s’annulent l’une l’autre, des partitions plus fines qui peuvent être rééquilibrées, l’exclusion temporaire des machines lentes, et le renvoi d’un résultat suffisamment bon quand la plupart des shards, mais pas tous, ont répondu. Dans un banc d’essai qu’ils décrivent, déclencher la couverture après 10 ms a fait passer la latence au 99,9e percentile de 1800 ms à 74 ms, en n’envoyant qu’environ 2 % de requêtes en plus.
La loi de Little : combien de requêtes sont en cours
La loi de Little, démontrée par John Little en 1961, est la formule la plus utile en planification de la capacité. Pour tout système stable, le nombre moyen d’éléments qu’il contient (L) est égal au taux d’arrivée moyen (λ, lambda) multiplié par le temps moyen que chaque élément passe à l’intérieur (W) : L = λ × W. Elle est vraie quels que soient le profil des arrivées ou l’ordre de service, à condition que le système soit stable (en moyenne, autant d’éléments sortent qu’il en arrive) et que vous utilisiez des unités cohérentes.
Pour un logiciel, L est le nombre de requêtes en cours simultanément (la concurrence), λ est le débit de requêtes et W est le temps de réponse. Si un service reçoit 200 requêtes par seconde et que chacune y passe 0,25 seconde, alors en moyenne 200 × 0,25 = 50 requêtes sont en cours à tout instant. Ce nombre détermine les pools de threads, les pools de connexions, la mémoire et le nombre de licences. La loi fonctionne aussi à rebours : si vous pouvez traiter au plus 50 requêtes à la fois et que chacune prend 0,25 s, vous ne pouvez pas soutenir plus de 50 ÷ 0,25 = 200 requêtes par seconde.
Exemple commenté
Exemple commenté : dimensionner un pool de connexions à une base de données avec la loi de Little
Le service des trajets d’une application de transport avec chauffeur traite 1800 requêtes par seconde au pic du soir. Chaque requête fait des appels à la base de données qui occupent une connexion pendant 40 ms au total. Chacune des 6 instances du service dispose d’un pool de 10 connexions. Est-ce suffisant ?
| Taux d’arrivée λ | 1800 requêtes par seconde |
|---|---|
| Temps d’occupation d’une connexion W | 40 ms = 0,040 s |
| Connexions utilisées L = λ × W | 1800 × 0,040 = 72 connexions occupées en moyenne |
| Connexions disponibles | 6 instances × 10 = 60 |
| Verdict | 72 > 60. Les requêtes feront la queue pour obtenir une connexion, le temps d’attente augmentera sans limite au pic, et les temps de réponse grimperont jusqu’à ce que les délais d’expiration se déclenchent. |
| Objectif avec marge | Visez un taux d’utilisation du pool d’environ 70 % au plus : 72 ÷ 0,7 ≈ 103 connexions, soit environ 18 par instance |
| Vérifier la base de données | Le serveur de base de données doit aussi accepter 103 connexions ; sinon, réduisez W (requêtes plus rapides, moins d’allers-retours) ou le nombre d’appels à la base par requête |
| Et si les requêtes deviennent plus rapides ? | Ramener W à 25 ms donne L = 1800 × 0,025 = 45 connexions, ce qui tient dans les 60 existantes avec un taux d’utilisation de 75 % |
La loi de Little convertit un objectif de débit et une latence en un chiffre de concurrence que l’on peut comparer directement à une limite configurée.
Elle donne des moyennes. Les rafales poussent le nombre instantané plus haut, c’est pourquoi l’exemple ajoute une marge au lieu de dimensionner à exactement 72.
Taux d’utilisation et files d’attente : pourquoi les systèmes s’effondrent brutalement
Le taux d’utilisation (ρ, rho) est la fraction du temps pendant laquelle une ressource est occupée. Un serveur qui reçoit 40 requêtes par seconde, nécessitant chacune 20 ms de service, est occupé 40 × 0,020 = 0,8 du temps : il est utilisé à 80 %. L’intuition suggère qu’une ressource occupée à 80 % ne devrait être qu’un peu plus lente qu’une ressource occupée à 50 %. La théorie des files d’attente dit le contraire, car les requêtes n’arrivent pas de façon régulière ; elles se regroupent, et quand elles se regroupent, les dernières attendent.
Le modèle le plus simple est la file M/M/1 : arrivées aléatoires, temps de service aléatoires et un seul serveur. Son temps de réponse moyen est W = S ÷ (1 − ρ), où S est le temps de service moyen. À 50 % d’utilisation, W = 2S. À 80 %, W = 5S. À 90 %, W = 10S. À 99 %, W = 100S. Le dénominateur tend vers zéro à mesure que le taux d’utilisation approche 100 %, si bien que le temps de réponse augmente sans limite. Les systèmes réels ne sont pas exactement des M/M/1, et les pools multiserveurs se comportent mieux au même taux d’utilisation, mais la forme est universelle : plate, puis un coude, puis une falaise.
File M/M/1 : temps de réponse moyen pour un temps de service de 20 ms
| Taux d’utilisation ρ | W = S ÷ (1 − ρ) | Temps de réponse moyen | Dont attente |
|---|---|---|---|
| 50% | 20 ÷ 0.5 | 40 ms | 20 ms |
| 70% | 20 ÷ 0.3 | 67 ms | 47 ms |
| 80% | 20 ÷ 0.2 | 100 ms | 80 ms |
| 90% | 20 ÷ 0.1 | 200 ms | 180 ms |
| 95% | 20 ÷ 0.05 | 400 ms | 380 ms |
| 99% | 20 ÷ 0.01 | 2000 ms | 1980 ms |
La falaise du taux d’utilisation
Les leçons pratiques sont directes. Ne prévoyez pas de faire tourner des ressources critiques à près de 100 % d’occupation ; gardez une marge pour que les rafales ne vous fassent pas passer le coude. Surveillez ensemble le taux d’utilisation et la longueur des files (la méthode USE de Brendan Gregg vérifie l’utilisation, la saturation et les erreurs de chaque ressource). Et n’oubliez pas que la falaise rend les petits changements dangereux : à 90 % d’utilisation, une hausse du trafic d’environ 6 % seulement vous amène à 95 % et double le temps de réponse moyen. Le chapitre 9 traite des files d’attente, de la back pressure et de la planification de la capacité dans Asynchronisme, files d’attente et back pressure.
Scalabilité : charge, élasticité, scalabilité verticale et horizontale
La scalabilité est la capacité à absorber plus de charge en ajoutant des ressources, idéalement avec une capacité qui croît proportionnellement et sans refonte. Elle n’a de sens qu’une fois que l’on a précisé ce qu’est la « charge ». Les paramètres de charge diffèrent d’un système à l’autre : requêtes par seconde pour une API, connexions simultanées pour un service de messagerie instantanée, ratio lectures/écritures pour un réseau social, volume de données pour une plateforme d’analytique, ou nombre d’abonnés du compte le plus suivi pour un fil d’actualité. Un scénario de scalabilité nomme le paramètre de charge, la croissance et la réponse qui doit être préservée : « quand les commandes quotidiennes passent de 50 000 à 500 000, la latence p95 du paiement reste inférieure à 1,5 s grâce à l’ajout d’instances, avec un coût par commande au plus 20 % plus élevé ».
L’élasticité ajoute le temps : la capacité à ajouter et retirer de la capacité automatiquement à mesure que la charge monte et descend, pour qu’un site de billetterie puisse grandir pour une mise en vente et rétrécir ensuite. C’est une grande partie de l’argument économique en faveur des plateformes cloud. Il existe deux façons fondamentales d’ajouter de la capacité. La scalabilité verticale (scaling up) donne à une machine plus de CPU, de mémoire ou un stockage plus rapide ; elle est simple, ne demande aucune modification du logiciel, et se heurte à des limites strictes et à des prix croissants. La scalabilité horizontale (scaling out) ajoute des machines et répartit le travail ; elle n’a pas de plafond fixe, mais exige que le logiciel soit conçu pour cela, avec des instances sans état, de l’équilibrage de charge et des données partitionnées. Le chapitre 9 traite des stratégies en détail dans Stratégies de passage à l’échelle : verticale, horizontale et cube de scalabilité.
La loi d’Amdahl : la limite du parallélisme
Ajouter des processeurs ou des nœuds n’aide que pour le travail qui peut se faire en parallèle. La loi d’Amdahl, tirée de l’article de Gene Amdahl de 1967 sur les limites du calcul parallèle, exprime cette idée. Si une fraction s du travail est séquentielle (elle doit se faire une étape après l’autre) et que le reste, 1 − s, peut être réparti parfaitement sur N processeurs, la meilleure accélération possible est accélération(N) = 1 ÷ (s + (1 − s) ÷ N). Quand N croît sans limite, l’accélération tend vers 1 ÷ s. Avec 10 % de travail séquentiel, aucun nombre de processeurs ne peut rendre la tâche plus de dix fois plus rapide.
Exemple commenté
Exemple commenté : un traitement de facturation nocturne
Le traitement de facturation nocturne d’un opérateur télécom prend 8 heures sur un serveur. Le profilage montre que 10 % du temps est séquentiel (chargement des tarifs, puis écriture d’un fichier récapitulatif unique) ; les 90 % restants valorisent les appels de chaque client indépendamment. Le traitement doit se terminer en moins de 1,5 heure. Combien de serveurs faut-il ?
| Fraction séquentielle s | 0,10 ; fraction parallèle 0,90 |
|---|---|
| Accélération nécessaire | 8 h ÷ 1.5 h ≈ 5.33 |
| N = 4 | 1 ÷ (0,10 + 0,90 ÷ 4) = 1 ÷ 0,325 ≈ 3,08 ; durée 8 ÷ 3,08 ≈ 2,6 h |
| N = 8 | 1 ÷ (0,10 + 0,1125) = 1 ÷ 0,2125 ≈ 4,71 ; durée ≈ 1,70 h |
| N = 16 | 1 ÷ (0,10 + 0,05625) = 1 ÷ 0,15625 = 6,4 ; durée = 1,25 h |
| Plus petit N qui atteint l’objectif | Il faut 0,10 + 0,90 ÷ N ≤ 1 ÷ 5,33 = 0,1875, donc N ≥ 0,90 ÷ 0,0875 ≈ 10,3 : 11 serveurs donnent 1 ÷ (0,10 + 0,0818) ≈ 5,50, durée ≈ 1,45 h |
| N = 64 | 1 ÷ (0,10 + 0,0141) ≈ 8,77 ; durée ≈ 0,91 h |
| Limite quand N croît | 1 ÷ 0,10 = 10 ; durée jamais inférieure à 0,8 h |
| Variante | Réduire de moitié la partie séquentielle (s = 0,05), par exemple en chargeant les tarifs en parallèle. Alors 8 serveurs donnent 1 ÷ (0,05 + 0,11875) ≈ 5,93, durée ≈ 1,35 h : l’objectif est atteint avec 8 serveurs au lieu de 11. |
Passer de 16 à 64 serveurs (quatre fois le coût) ne réduit la durée que de 1,25 h à 0,91 h. Les rendements diminuent vite.
Réduire la fraction séquentielle coûte souvent bien moins cher qu’ajouter du matériel. Les architectes traquent les parties séquentielles cachées : un verrou unique, une ligne de base de données que tout le monde met à jour, un coordinateur central.
La loi universelle de scalabilité
La loi d’Amdahl suppose que des processeurs supplémentaires n’aggravent jamais les choses ; l’accélération se contente de plafonner. Les systèmes distribués réels, eux, se dégradent souvent : au-delà d’un certain point, ajouter des nœuds réduit le débit. La loi universelle de scalabilité (USL) de Neil Gunther modélise ce phénomène avec deux pénalités. La contention (α, alpha) est le coût de l’attente pour accéder aux ressources partagées, le même effet que la fraction séquentielle d’Amdahl. La cohérence (β, bêta) est le coût du maintien de la cohérence des données entre les nœuds : chaque nœud doit échanger des informations avec tous les autres, si bien que ce coût croît à peu près comme le carré du nombre de nœuds.
La capacité relative est C(N) = N ÷ (1 + α(N − 1) + βN(N − 1)). Avec β = 0, elle se ramène à la loi d’Amdahl. Avec tout β supérieur à zéro, la capacité atteint un pic vers N = √((1 − α) ÷ β), puis diminue. Pour α = 0,03 et β = 0,002, le pic se situe vers √(0,97 ÷ 0,002) ≈ 22 nœuds, pour une capacité d’environ 8,6 fois celle d’un nœud ; à 32 nœuds, la capacité est retombée à environ 8,2. Gunther décrit le modèle dans Guerrilla Capacity Planning (2007). Son intérêt pratique est que α et β peuvent être ajustés à partir d’une poignée de mesures de tests de charge, ce qui permet de prévoir à quel point le passage à l’échelle cessera d’être rentable avant d’acheter le matériel.
Scalabilité linéaire, d’Amdahl et USL
Tactiques de performances : vue d’ensemble
Bass, Clements et Kazman regroupent les tactiques de performances en deux familles. La première consiste à maîtriser la demande de ressources : réduire la quantité de travail qui arrive ou le coût de chaque unité. La seconde consiste à gérer les ressources : faire travailler davantage les ressources disponibles ou en ajouter. La plupart des conceptions réelles combinent plusieurs tactiques ; chacune a aussi des coûts ailleurs, ce qui explique qu’il s’agisse de décisions architecturales.
Les tactiques de performances et où le cours les traite
| Tactique | Ce qu’il fait | Coût ou risque typique | Pour en savoir plus |
|---|---|---|---|
| Réduire la demande : limitation de débit (rate limiting), contrôle d’admission, salles d’attente virtuelles | Plafonne ou lisse la charge entrante pour que le cœur reste en deçà de son coude | Certains utilisateurs sont retardés ou refusés | Chapitre 9 : files d’attente et back pressure |
| Réduire le travail par requête : algorithmes efficaces, charges utiles plus petites, moins d’allers-retours | Réduit le temps de service | Effort de développement ; code parfois moins souple | Chapitre 0 : données et algorithmes |
| Prioriser et ordonnancer | Sert d’abord le travail important (les commandes payées avant les rapports) | Le travail peu prioritaire risque la famine | Chapitre 9 : patrons de résilience |
| Mise en cache | Garde les résultats près de l’utilisateur pour éviter de refaire le travail | Données périmées, complexité de l’invalidation, coût en mémoire | Chapitre 7 : mise en cache |
| Concurrence et asynchronisme | Traite en parallèle le travail indépendant ; retire le travail lent du chemin de la requête | Raisonnement plus difficile, problèmes d’ordre et de cohérence | Chapitre 6 : communication |
| Réplication | Ajoute des copies de services ou de données pour répartir les lectures et la charge | Cohérence entre les copies ; coût | Chapitre 7 : réplication et partitionnement |
| Partitionnement (sharding) | Divise les données et le travail pour que chaque nœud en traite une part | Les requêtes inter-partitions et le rééquilibrage sont difficiles | Chapitre 7 : réplication et partitionnement |
| Ajouter des ressources : scalabilité verticale ou horizontale | Augmente la capacité | Coût ; limité par le travail séquentiel et de cohérence | Chapitre 9 : stratégies de passage à l’échelle |
Vérifiez votre compréhension
Un service reçoit 500 requêtes par seconde et chacune prend 80 ms. Combien sont en cours en moyenne ?
D’après la loi de Little, L = 500 × 0,08 = 40 requêtes en cours.
Avec la formule M/M/1, quel est le temps de réponse moyen d’un serveur dont le temps de service est de 10 ms, à 75 % d’utilisation ?
W = 10 ÷ (1 − 0,75) = 40 ms, dont 30 ms d’attente.
Pourquoi le p99 est-il généralement une meilleure mesure pour une exigence que la moyenne ?
Les distributions des temps de réponse sont asymétriques ; la moyenne peut paraître satisfaisante alors qu’une part non négligeable des utilisateurs subit des réponses très lentes, et le p99 met cette queue en évidence.
Si 20 % d’une tâche est séquentielle, quelle est l’accélération maximale obtenue en ajoutant des processeurs ?
1 ÷ 0,2 = 5 fois, quel que soit le nombre de processeurs ajoutés.
Sources et lectures complémentaires
Une question sur cette section ? ArchitectBot, l’assistant d’étude IA du cours, répond d’abord à partir du cours.
Demander : « Notre API a une moyenne de 150 ms, mais les utilisateurs se plaignent encore qu’elle est lente. Comment mesurer ses performances et leur fixer des objectifs ? »