Mesurer les limites : mémoire, disque et débit
Ce que ce chapitre apporte 5 points
- Distinguer les trois murs que rencontre un traitement de données : la mémoire, le stockage, le débit.
- Mesurer le coût réel d'une opération plutôt que le supposer.
- Reconnaître le passage d'un coût linéaire à un coût qui ne l'est plus.
- Repérer l'accident de conception qui transforme un traitement d'une seconde en un traitement d'une heure.
- Interpréter les cinq V, et dire ce qu'ils décrivent bien et ce qu'ils n'orientent pas.
Le big data commence là où les outils ordinaires cèdent. On le décrit souvent par cinq V, volume, vélocité, variété, véracité et valeur, mais la question utile est ailleurs : il n'existe aucun seuil en gigaoctets. Une donnée devient grosse le jour où une opération qui marchait cesse de marcher, et ce jour-là dépend de l'opération bien plus que du volume. Trois murs différents attendent, la mémoire, le disque et le débit, et chacun appelle un remède qui ne soigne pas les deux autres. Les confondre fait acheter des machines pour un problème d'algorithme.
Un fichier de trois gigaoctets se lit sans difficulté sur un ordinateur portable si l'on se contente d'en compter les lignes. Le même fichier peut faire tomber la même machine si l'on veut trier son contenu en le chargeant d'un bloc. Rien n'a changé dans les données : ce qui a changé, c'est ce qu'on leur demande, et la façon de le demander, puisqu'un tri par morceaux, comme celui de la commande sort, en vient à bout avec quelques mégaoctets.
C'est pourquoi la question « à partir de quelle taille parle-t-on de grandes données ? » n'a pas de réponse, et pourquoi il faut la remplacer par une autre : quelle opération, sur quelle machine, cesse de tenir ses promesses, et pourquoi. Ce chapitre pose cette question et donne de quoi y répondre par la mesure plutôt que par l'intuition.
Trois murs, trois remèdes
Le volume seul ne dit rien, la contrainte qui saute dit tout. La mémoire vive. Le traitement charge tout en mémoire, et la mémoire est pleine. Le programme s'arrête net, ou se met à échanger avec le disque et devient mille fois plus lent. Remède : ne plus tout charger, donc traiter en flux.
Le stockage. Les données ne tiennent plus sur un disque, ou plus sur une machine. Remède : les répartir sur plusieurs machines, ce qui pose aussitôt la question de savoir lesquelles vont où.
Le débit. Les données arrivent plus vite qu'on ne les traite. Le retard s'accumule, et rien ne le rattrapera puisque le flux ne s'arrête pas. Remède : traiter en parallèle, ou traiter moins.
Ces trois murs sont indépendants. Un capteur qui produit cent mesures par seconde ne posera jamais de problème de stockage, et posera un problème de débit dès que le traitement de chaque mesure dépassera dix millisecondes. Un historique de dix ans tient sur un disque de bureau et fait exploser la mémoire au premier tri qui le charge tout entier.
Les tailles s'écrivent ici, comme dans tout le parcours, avec les préfixes du Système international : un kilooctet (ko) vaut mille octets, un mégaoctet (Mo) un million, un gigaoctet (Go) un milliard, un téraoctet (To) mille milliards. Les multiples de 1 024 portent d'autres noms, le kibioctet (Kio), le mébioctet (Mio) et le gibioctet (Gio), et ne servent que là où le binaire compte vraiment, comme la capacité d'une mémoire vive : une barrette de 16 Gio contient 17,2 milliards d'octets. L'écart, 2,4 % au kilo et 7,4 % au giga, ne change aucun ordre de grandeur, mais il suffit à fausser un calcul qui mélange les deux.
Le mur de la mémoire prévient : le programme s'arrête avec une erreur claire. Le mur du débit prévient aussi : la file d'attente grandit sous les yeux de tout le monde.
Le troisième cas ne prévient pas. Un traitement dont le coût croît plus vite que les données continue de fonctionner, de plus en plus lentement, sans jamais rien signaler. Il passe les essais sur un échantillon, il passe la recette sur un jeu réduit, et il tombe le jour où le volume réel arrive. C'est le sujet de la deuxième moitié de ce chapitre.
Un fichier de 50 Go doit être compté ligne par ligne sur une machine de 16 Gio de mémoire. Que se passe-t-il ?
Mesurer plutôt que supposer
Une liste de un million d'entiers en Python, cela pèse combien, et cela coûte combien à parcourir ? La réponse ne s'estime pas : elle se mesure.
Deux enseignements, et le second compte plus que le premier.
Le poids réel dépasse largement ce que sys.getsizeof annonce, parce que la liste ne contient pas les nombres mais des références vers eux. Dans la page, où Python tourne en 32 bits, un million d'entiers occupe une vingtaine de mégaoctets : quatre pour les références, seize pour les objets. Un Python 64 bits ordinaire en compte trente-six. Un tableau numpy range le même million dans quatre mégaoctets ici, huit ailleurs : le rapport de quatre à cinq n'est pas anecdotique quand la mémoire est la contrainte qui saute.
Surtout, le temps de la somme croît comme le nombre de valeurs. Deux fois plus de données, deux fois plus de temps, aux aléas de la mesure près. C'est la propriété la plus précieuse qu'un traitement puisse avoir, et le chapitre suivant est consacré à la préserver.
Un traitement a un coût linéaire quand doubler la quantité de données double le temps de traitement, sans plus. On l'écrit O(n).
C'est le régime qui passe à l'échelle sans surprise : à coût linéaire, un volume mille fois plus grand demande mille fois plus de temps, ce qui est déjà beaucoup mais reste prévisible et se répartit sur plusieurs machines. Un coût en n log n, celui d'un tri, s'en écarte à peine et se répartit aussi. Au-delà, dès le coût quadratique, la répartition elle-même ne suffit plus : doubler les machines quand les données doublent laisse encore le temps doubler.
Où le coût cesse d'être linéaire
Compter, sommer et chercher un maximum coûtent linéaire. Trier ne le fait pas, et la différence se voit dès que les tailles grandissent.
Le rapport d'un palier au suivant tourne autour de deux pour le tri, et le dépasse un peu. C'est la signature de n log n : plus cher que linéaire, mais de très peu, et parfaitement utilisable jusqu'à des volumes considérables. La somme, elle, ne dure ici que de quelques dixièmes de milliseconde à une ou deux millisecondes : l'horloge de la page, qui ne distingue pas mieux que le dixième de milliseconde, départage mal des durées si courtes, et ses rapports sautent d'une exécution à l'autre. Le bloc précédent, sur des listes dix fois plus longues, l'a montrée linéaire.
Le code retient le meilleur de plusieurs essais et non leur moyenne. Une mesure peut être ralentie par n'importe quoi d'extérieur au programme, jamais accélérée : le minimum est donc l'estimation la moins polluée du coût réel. Sans cette précaution, les rapports observés sautent d'un palier à l'autre et ne veulent plus rien dire.
Nul besoin de savoir démontrer une complexité pour la reconnaître. Doubler la taille et regarder le rapport des temps suffit :
- rapport 2 : coût linéaire, tout va bien ;
- rapport un peu plus de 2 : n log n, c'est le tri, c'est acceptable ;
- rapport 4 : coût quadratique, et le traitement ne passera pas à l'échelle ;
- rapport 8 : coût cubique, il faut changer d'algorithme, pas de machine.
Cette mesure prend quelques minutes et évite des semaines d'optimisation au mauvais endroit.
1.Un traitement passe de 10 s à 40 s quand les données doublent. Son coût est…
2.Remplacer x in liste par x in ensemble dans une boucle…
L'accident qui multiplie tout par mille
Le tri est cher et se voit. L'accident qui suit ne se voit pas, et il est de très loin la première cause de traitements qui ne finissent pas.
Les deux boucles sont écrites de la même façon, produisent le même résultat, et diffèrent d'un mot : list contre set. Le rapport de temps se compte en centaines, voire en milliers selon la machine. Il reste pourtant bien en dessous du nombre de comparaisons évitées, plus de seize mille par test en moyenne : une recherche dans un ensemble coûte le calcul d'une empreinte, qui vaut à lui seul plusieurs comparaisons. L'écart, lui, grandit avec la taille de la référence, et c'est tout le danger.
x in liste parcourt la liste jusqu'à trouver, donc coûte proportionnellement à sa longueur. Placé dans une boucle sur les données, il transforme un traitement linéaire en traitement quadratique.
Sur un échantillon de mille lignes contre une référence de mille lignes, cela fait un million de comparaisons : personne ne remarque rien. Sur un million de lignes contre un million de lignes, cela fait mille milliards de comparaisons, et le traitement ne finira pas.
x in ensemble et x in dictionnaire répondent en temps constant, quelle que soit la taille. La correction tient en un mot, encore faut-il savoir le chercher.
Le même piège se déguise sous d'autres formes. Concaténer dans une boucle avec chaine = chaine + morceau recopie toute la chaîne à chaque tour. Insérer en tête d'une liste avec insert(0, x) décale tout le contenu. Rechercher une correspondance entre deux fichiers avec deux boucles imbriquées revient au même calcul que le in ci-dessus, en plus visible.
Le volume ne crée pas le problème, il le révèle. Un traitement quadratique rend le bon résultat sur mille lignes, mais il fait un million d'opérations là où mille auraient suffi. Simplement, un million d'opérations prend quelques millisecondes et personne ne s'en aperçoit.
Les grandes données ne créent donc pas les problèmes de conception. Elles les rendent visibles, et souvent au pire moment. C'est une raison suffisante pour mesurer les coûts avant que le volume n'arrive.
Le débit, un mur d'une autre nature
Les deux premières sections traitent d'un jeu de données posé quelque part, qu'on a le temps de traiter. Beaucoup de situations ne ressemblent pas à cela : les données arrivent, en continu, et ne s'arrêtent pas.
Ce mur-là se calcule sans machine. Un traitement qui demande dix millisecondes par mesure absorbe cent mesures par seconde, pas une de plus. Si le capteur en produit cent vingt, le retard grandit de vingt mesures par seconde, soit 1 728 000 mesures de retard au bout d'une journée. Aucune optimisation ne rattrape un retard qui grandit : il faut soit descendre sous le seuil, soit traiter en parallèle, soit accepter de ne pas tout traiter.
Le débit soutenable d'un traitement est le nombre d'éléments qu'il absorbe par unité de temps sans accumuler de retard. Il vaut l'inverse du temps de traitement d'un élément, multiplié par le nombre de traitements menés en parallèle.
Un système est stable tant que le débit d'arrivée reste sous le débit soutenable. Au-dessus, la file d'attente croît sans limite, et le système est déjà en panne même si rien ne l'indique encore.
L'intuition courante veut qu'un pic de charge se résorbe une fois passé. C'est vrai d'un pic, faux d'un dépassement durable.
Tant que le débit d'arrivée dépasse le débit soutenable, ne serait-ce que de quelques pour cent, la file grandit linéairement et sans fin. Les seules issues sont d'augmenter le débit soutenable, de réduire le débit d'arrivée, ou de jeter des données de façon assumée plutôt que subie.
Les données arrivent à 500 par seconde, le débit soutenable est de 480. Que faire ?
Sous le débit soutenable, la file ne grandit plus sans fin, mais elle ne reste pas vide pour autant. Le calcul qui précède suppose des arrivées régulières et des traitements de durée fixe. Un serveur d'ingestion, qui reçoit les lots de journaux d'un parc de machines, ne voit rien de tel : les lots arrivent quand les machines les envoient, par grappes et par creux, et leur traitement dure plus ou moins longtemps selon leur taille. Chaque grappe forme une file que le creux suivant doit résorber, et plus le serveur est chargé, plus les creux se raréfient.
Mesure encore trop courte pour être comparée : 0 clients servis. Laisser tourner, ou accélérer, jusqu'à la centaine. Un écart entre la mesure et la théorie avant ce point ne dit rien de la théorie, il dit seulement qu'on n'a pas assez regardé.
Monter λ à 18, puis à 19, puis à 19,5, en remettant à zéro après chaque réglage. L'arrivée ne gagne que 3,5 lots par seconde, mais le séjour annoncé passe de 250 à 500, 1 000 puis 2 000 ms, et les lots présents de 4 à 9, 19 puis 39 : chaque fois que l'écart entre μ et λ est divisé par deux, l'attente double. À 19, la ligne « présents, mesuré » met de longues minutes simulées à se rapprocher de 19, parce que chaque vague du tracé met longtemps à se vider.
Pousser λ à 20, exactement le débit soutenable : la figure affiche ρ = 1, « instable », et n'annonce plus aucune moyenne. Le calcul sans hasard ne voyait là aucun retard.
Revenir à λ = 19 et passer à c = 2 serveurs : avec une seconde machine, le séjour annoncé tombe de 1 000 à 65 ms.
Le modèle de la figure, nommé M/M/1, tient en deux formules. Le temps moyen qu'un lot passe dans le système, attente et traitement compris, vaut , et le nombre moyen de lots présents , où est le débit d'arrivée et le débit soutenable. Tout se joue au dénominateur : l'attente dépend moins de la charge que de l'écart qui reste avant la saturation, et cet écart fond à l'approche du seuil. À 50 % de charge, un lot passe dans le système deux fois la durée moyenne de son traitement ; à 90 %, dix fois ; à 99 %, cent fois.
Un serveur ne se dimensionne jamais à 100 % de son débit. La marge se déduit de l'attente acceptable, et non d'un pourcentage fixé d'avance. Avec des lots de 50 millisecondes et un séjour d'au plus 250 millisecondes, le serveur ne doit pas dépasser 80 % de charge. Au-delà, il tient sur le papier et fait attendre en pratique ; à 100 %, son attente moyenne n'a plus de limite.
Le chapitre Files d'attente et dimensionnement du parcours de sciences appliquées établit ces formules, et montre ce que changent la régularité des traitements et une file commune à plusieurs serveurs.
Les cinq V, et ce qu'ils valent
Le domaine s'est doté d'un vocabulaire commode, souvent présenté comme une définition. Il mérite d'être connu, et il mérite d'être relativisé.
On lit couramment que les grandes données se caractérisent par plusieurs V : le volume, la vélocité, c'est-à-dire le débit, la variété des formats et des sources, la véracité de données dont la qualité n'est pas garantie, et parfois la valeur qu'on espère en tirer.
Cette liste décrit bien le paysage. Elle ne dit rien de ce qu'il faut faire, pour une raison simple : elle range les difficultés par leur apparence et non par leur remède. Le volume et le débit appellent des solutions opposées, la variété relève de la modélisation et non de l'infrastructure, et la véracité est un problème de préparation des données, traité dans le chapitre sur la préparation du parcours d'intelligence artificielle.
Devant un traitement dont on craint qu'il ne passe pas à l'échelle, trois questions valent mieux que cinq mots :
- Qu'est-ce qui sature en premier quand le volume est multiplié par dix : la mémoire, le disque, le processeur, le réseau ?
- Le coût est-il linéaire ? Doubler les données double-t-il le temps, ou davantage ?
- Le traitement peut-il se faire en une seule lecture, ou faut-il tout garder ?
La troisième question est celle du chapitre suivant, et elle décide de presque tout.
Quel est le principal défaut de la description par les cinq V ?
Mettre en pratique
Les trois murs ne se devinent pas, ils se chiffrent, et le chiffre se pose avant d'écrire la première ligne de traitement.
Mesurer le coût avant de le subir
- 1.
Un fichier de 40 gigaoctets est traité sur une machine qui ne peut en charger que 2 à la fois. En combien de morceaux, au moins, faut-il le découper pour le lire tout entier sans dépasser la mémoire ?
- 2.
Ce même fichier est lu depuis un disque qui débite 200 mégaoctets par seconde. Combien de secondes la lecture demande-t-elle, à débit constant ?
- 3.
Un tri en temps quadratique et un tri en temps sont comparés sur 100 000 lignes. Par quel facteur le premier dépasse-t-il le second en nombre d'opérations ? Une valeur à 5 % près suffit.
- 4.
Une machine exécute un milliard d'opérations par seconde. À partir de combien de MILLIONS de lignes un traitement quadratique dépasse-t-il une heure ? Donner la valeur à un chiffre après la virgule.
La dernière réponse est celle qui surprend. Moins de deux millions de lignes suffisent à faire basculer un traitement quadratique d'une seconde à une heure, et deux millions de lignes n'ont rien d'un gros volume. Le mur n'est pas dans la taille des données, il est dans la forme de l'algorithme.
Sur mille lignes, un traitement quadratique…
Un fichier de journalisation contient 400 millions de lignes, chacune pesant environ 120 octets. Un traitement doit compter les lignes contenant le mot ERREUR. La machine dispose de 16 Gio de mémoire vive. Quel mur risque de sauter, et lequel ne sautera pas ?
Le fichier pèse environ 48 gigaoctets, près de trois fois les 17 milliards d'octets de la mémoire : il ne tient pas en mémoire. Le mur de la mémoire saute si, et seulement si, le traitement charge tout, ce qu'une lecture ligne par ligne évite complètement : compter des lignes ne demande de retenir qu'un compteur.
Le mur du stockage ne saute pas : 48 gigaoctets tiennent sur n'importe quel disque contemporain.
Le coût reste linéaire, et le temps sera dominé par la lecture du disque et non par le calcul. C'est un cas de grandes données au sens du volume, et pourtant une simple boucle le traite sur une seule machine.
Un traitement met 40 secondes sur 100 000 lignes et 160 secondes sur 200 000 lignes. Combien de temps prendra-t-il sur 10 millions de lignes ?
Doubler les données quadruple le temps : le coût est quadratique.
Passer de 200 000 à 10 millions multiplie la taille par 50, donc le temps par 2 500. Cela fait 160 × 2 500 = 400 000 secondes, soit un peu plus de quatre jours et demi.
La conclusion n'est pas qu'il faut une machine 2 500 fois plus rapide : c'est qu'il faut trouver le in dans la boucle. Le temps de la version corrigée ne se déduit pas en prolongeant celui-ci en ligne droite : 160 × 50 secondes supposerait qu'elle coûte autant par ligne que la version quadratique, qui fait une recherche dans une longue liste à chaque ligne. Une recherche dans un ensemble coûte une empreinte, et la version corrigée traite les 10 millions de lignes en quelques secondes ou quelques minutes.
Un capteur émet 500 mesures par seconde. Le traitement d'une mesure prend 4 millisecondes. Le système tient-il ? Et si le traitement passe à 1,8 milliseconde ?
À 4 millisecondes par mesure, le débit soutenable vaut 250 mesures par seconde. L'arrivée en apporte 500 : le retard grandit de 250 mesures par seconde, soit 21,6 millions de mesures par jour. Le système ne tient pas, et aucune patience ne le rattrapera.
À 1,8 milliseconde, le débit soutenable monte à 555 mesures par seconde. Le système tient, avec une marge de 10 %. Cette marge est faible : le moindre pic la consomme, et il faudra soit la surveiller, soit paralléliser sur deux traitements pour doubler le débit soutenable.
Deux propositions pour accélérer un traitement quadratique de 4 heures. La première double le nombre de machines. La seconde remplace une recherche dans une liste par une recherche dans un dictionnaire. Laquelle choisir ?
La seconde, sans hésiter, et l'écart n'est pas de degré mais de nature.
Doubler les machines divise le temps par deux au mieux, donc ramène quatre heures à deux heures, et coûte deux fois plus cher indéfiniment.
Passer d'un coût quadratique à un coût linéaire divise le temps par un facteur qui croît avec la taille de la référence : à chaque ligne, une recherche qui parcourait en moyenne des centaines de milliers d'éléments laisse place au calcul d'une empreinte. Sur un million d'éléments, le facteur se compte en dizaines, voire en centaines de milliers, et il ne coûte rien.
La règle générale tient en une phrase : acheter des machines pour un problème d'algorithme revient à payer pour ne pas corriger le code.
Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?
Dans la bibliothèque d'exercices
Temps de lecture, débit agrégé, et nombre de machines nécessaires.
- Le temps de lire un fichierNiveau 1
- Le débit de plusieurs machinesNiveau 1
- Combien de machines pour tenir le délaiNiveau 2
- Le rapport des temps, et ce qu'il annonceNiveau 2
- Le débit qu'un traitement soutientNiveau 2