Explorez les idées d'architecture de John Hennessy : pourquoi la performance a cessé de croître « gratuitement », comment le parallélisme aide, et quels compromis façonnent les systèmes modernes.

John Hennessy est l'un des architectes qui a le mieux expliqué pourquoi les ordinateurs deviennent plus rapides — et pourquoi ce progrès peut parfois s'arrêter. Au-delà de la création de processeurs influents et de la popularisation des idées RISC, il a offert aux concepteurs de systèmes un vocabulaire pratique pour les décisions de performance : quoi optimiser, quoi ne pas optimiser, et comment faire la différence.
Quand on parle de « mise à l'échelle des performances », on entend souvent « mon programme va plus vite ». Dans les systèmes réels, la mise à l'échelle est une négociation à trois entre vitesse, coût et consommation/énergie. Une modification qui rend une charge de travail 20 % plus rapide peut aussi rendre la puce plus chère, le serveur plus difficile à refroidir, ou vider la batterie plus vite. Le cadrage de Hennessy importe parce qu'il traite ces contraintes comme des entrées d'ingénierie normales — pas comme des surprises désagréables.
Le premier est le parallélisme : faire plus de travail en même temps. Il apparaît à l'intérieur d'un cœur (astuces au niveau des instructions), entre cœurs (threads), et entre machines entières.
Le deuxième est la spécialisation : utiliser l'outil adapté à la tâche. GPU, encodeurs vidéo et accélérateurs ML existent parce que les CPU généralistes ne peuvent pas tout faire efficacement.
Le troisième est le compromis : chaque « gain » a un prix. L'important est de comprendre où se situe la limite — calcul, mémoire, communication ou énergie.
Ce n'est pas une biographie approfondie. C'est plutôt un ensemble de concepts pratiques que vous pouvez appliquer en lisant des benchmarks, en choisissant du matériel, ou en concevant un logiciel qui doit croître avec la demande.
Pendant longtemps, les améliorations de performance semblaient presque automatiques. À mesure que les transistors rétrécissaient, les fondeurs pouvaient en placer davantage sur un processeur et souvent augmenter la fréquence. Les équipes logicielles pouvaient exécuter le même programme sur une nouvelle machine et le voir s'exécuter plus vite — sans refonte.
C'était la période où une nouvelle génération de CPU signifiait souvent plus de GHz, un coût par transistor plus bas et des accélérations visibles pour le code courant. Une grande partie de ces gains ne demandait pas aux développeurs de penser différemment : les compilateurs et les évolutions matérielles faisaient le travail lourd.
Finalement, augmenter la fréquence n'a plus été un simple avantage parce que la puissance et la chaleur augmentaient trop vite. Réduire la taille des transistors n'a pas automatiquement réduit la consommation comme avant, et pousser la fréquence rendait les puces plus chaudes. À un moment donné, la question n'était plus « Peut-on aller plus vite ? » mais « Peut-on refroidir et alimenter cela de manière fiable ? »
Pensez au moteur d'une voiture. Vous pouvez souvent aller plus vite en augmentant les tours — jusqu'à atteindre des limites : la consommation de carburant explose, les pièces surchauffent, et le système devient dangereux. Les CPU frappent une frontière similaire : augmenter les « RPM » (fréquence) coûte beaucoup plus d'énergie et produit plus de chaleur que le système ne peut gérer.
Quand l'échelle des fréquences a ralenti, la performance est devenue quelque chose qu'on mérite par la conception : plus de travail parallèle, meilleure utilisation des caches et de la mémoire, matériel spécialisé, et choix logiciels prudents. Le message de Hennessy s'inscrit dans ce changement : les gros gains viennent désormais de la façon dont l'ensemble du système — matériel et logiciel — coopère, et non d'une attente que la prochaine puce vous sauve automatiquement.
Le parallélisme au niveau des instructions (Instruction-Level Parallelism, ILP) consiste à faire plusieurs petites étapes en même temps à l'intérieur d'un seul cœur CPU. Même si votre programme est « mono-thread », le processeur peut souvent superposer du travail : pendant qu'une instruction attend quelque chose, une autre peut démarrer — si elles sont indépendantes.
Une façon simple d'imaginer l'ILP est le pipeline. Pensez à une chaîne de montage : une étape récupère une instruction, une autre la décode, une autre l'exécute, et une autre écrit le résultat. Une fois le pipeline rempli, le CPU peut terminer à peu près une instruction par cycle, même si chaque instruction prend plusieurs étapes pour traverser.
Le pipelining a aidé la performance pendant des années parce qu'il augmentait le débit sans obliger les programmeurs à réécrire tout.
Les programmes réels ne s'exécutent pas en ligne droite. Ils rencontrent des branches ("si ceci, alors cela"), et le CPU doit décider quoi récupérer ensuite. S'il attend de connaître la voie, le pipeline peut s'arrêter.
La prédiction de branche est la façon dont le CPU devine le chemin suivant pour que le travail continue. Quand la prédiction est correcte, la performance reste élevée. Quand elle se trompe, le CPU abandonne le travail effectué sur le mauvais chemin et paie une pénalité — cycles gaspillés et consommation d'énergie inutile.
Pousser l'ILP plus loin demande plus de matériel pour trouver des instructions indépendantes, les réordonner en toute sécurité, et récupérer des erreurs comme les prédictions de branche erronées. Cela ajoute de la complexité et des efforts de validation, augmente la consommation et offre souvent des gains plus modestes à chaque génération.
C'est l'une des leçons récurrentes de Hennessy : l'ILP est précieux, mais il atteint des limites pratiques — donc l'échelle de performance soutenue a besoin d'autres leviers, pas seulement d'une exécution mono-cœur « plus intelligente ».
La loi d'Amdahl rappelle que l'accélération d'une partie d'un travail ne peut pas accélérer l'ensemble au-delà de ce que la portion restante lente permet. Pas besoin de maths lourdes pour l'utiliser — il suffit de repérer ce qui ne peut pas être parallélisé.
Imaginez une épicerie avec un client et un processus de caisse :
Si le paiement prend toujours, disons, 10 % du temps total, même si vous rendez le scan « instantané » avec plus de caisses, vous ne pourrez pas dépasser environ un gain global de 10×. La partie sérielle devient le plafond.
La cuisine illustre le même modèle : vous pouvez couper les légumes pendant que l'eau chauffe (parallèle), mais vous ne pouvez pas « paralléliser » une cuisson qui doit rester 30 minutes au four.
L'idée clé est que les derniers pourcents de travail sériel limitent tout. Un programme « 99 % parallèle » semble fantastique — jusqu'à ce que vous l'étendiez sur de nombreux cœurs et découvriez que le 1 % sériel devient la jambe la plus longue.
La loi d'Amdahl explique pourquoi "ajouter des cœurs" déçoit souvent. Plus de cœurs aident seulement s'il y a suffisamment de travail parallèle et si les goulots sériels (synchronisation, E/S, phases mono-thread, blocages mémoire) restent petits.
Elle explique aussi pourquoi les accélérateurs peuvent être délicats : si un GPU accélère un noyau, mais que le reste du pipeline reste sériel, le gain global peut être modeste.
Avant d'investir dans le parallélisme, demandez : Quelle fraction est réellement parallèle, et qu'est-ce qui reste sériel ? Ensuite, investissez là où le temps est réellement passé — souvent le chemin « ennuyeux » sériel — car c'est lui qui fixe la limite.
Pendant des années, les gains de performance visaient surtout à rendre un seul cœur CPU plus rapide. Cette approche a atteint des limites pratiques : augmenter la fréquence augmentait la chaleur et la puissance, et les pipelines plus profonds ne se traduisaient pas toujours par des gains réels proportionnels. La réponse grand public a été de mettre plusieurs cœurs sur une puce et d'améliorer la performance en faisant plus de travail en parallèle.
Le multicœur aide de deux façons :
Cette distinction compte pour la planification : un serveur peut bénéficier immédiatement de la gestion de plus de requêtes simultanées, tandis qu'une application de bureau ne paraîtra plus rapide que si son propre travail peut être parallélisé.
Le parallélisme au niveau des threads n'est pas automatique. Le logiciel doit exposer du travail parallèle via des threads, des files de tâches, ou des frameworks qui découpent un travail en unités indépendantes. L'objectif est de garder les cœurs occupés sans qu'ils attendent constamment les uns les autres.
Des approches pratiques courantes incluent paralléliser des boucles, séparer des étapes indépendantes (par ex. décodage → traitement → encodage), ou gérer plusieurs requêtes/événements simultanément.
La montée en puissance multicœur bute souvent sur des frais généraux :
Le message plus large de Hennessy s'applique ici : le parallélisme est puissant, mais les vrais gains dépendent d'une conception système minutieuse et de mesures honnêtes — pas seulement d'ajouter des cœurs.
Un CPU ne peut travailler que sur des données qu'il a sous la main. Quand les données ne sont pas prêtes — parce qu'elles voyagent depuis la mémoire — le CPU doit attendre. Ce temps d'attente est la latence mémoire, et il peut transformer un processeur « rapide » en machine idle coûteuse.
Pensez à la mémoire comme à un entrepôt de l'autre côté de la ville. Même si vos ouvriers (les cœurs CPU) sont extrêmement rapides, ils ne peuvent rien assembler si les pièces sont coincées dans les embouteillages. Les processeurs modernes exécutent des milliards d'opérations par seconde, mais un aller-retour en mémoire principale peut prendre des centaines de cycles CPU. Ces pauses s'additionnent.
Pour réduire l'attente, les ordinateurs utilisent des caches, petites zones mémoire rapides proches du CPU — comme des étagères de proximité avec les pièces les plus utilisées. Quand les données nécessaires sont déjà sur l'étagère (un « cache hit »), le travail continue. Quand ce n'est pas le cas (un « miss »), le CPU doit aller les chercher plus loin et paie la pleine latence.
La latence est « combien de temps avant qu'arrive le premier élément ». La bande passante est « combien d'éléments peuvent arriver par seconde ». On peut avoir une grande bande passante (une autoroute large) mais une latence élevée (une longue distance). Certains workloads streament beaucoup de données (limités par la bande passante), tandis que d'autres ont besoin de petits morceaux épars (limités par la latence). Un système peut sembler lent dans les deux cas.
Le point de Hennessy concernant les limites apparaît ici comme le mur de la mémoire : la vitesse CPU s'est améliorée plus rapidement que les temps d'accès mémoire pendant des années, si bien que les processeurs perdaient du temps à attendre. C'est pourquoi les gains de performance viennent souvent d'une meilleure localité des données (pour que les caches soient efficaces), de repenser les algorithmes, ou de changer l'équilibre du système — pas seulement d'accélérer le cœur CPU.
Pendant longtemps, « plus rapide » signifiait surtout « augmenter la fréquence ». Cette mentalité casse quand on considère la puissance comme un budget strict plutôt qu'un détail après coup. Chaque watt supplémentaire se traduit par de la chaleur à évacuer, une batterie à vider, ou une facture d'électricité à payer. La performance reste un objectif — mais c'est la performance par watt qui décide ce qui peut être commercialisé et ce qui peut monter en échelle.
La puissance n'est pas qu'un détail technique ; c'est une contrainte produit. Un portable qui obtient de bons scores sur benchmark mais qui réduit sa fréquence après deux minutes donne l'impression d'être lent. Un téléphone qui rend une page instantanément mais perd 20 % de batterie en le faisant est un mauvais compromis. Même dans les serveurs, vous pouvez avoir des capacités de calcul libres mais pas de marge de puissance ou de refroidissement.
Augmenter la fréquence coûte disproportionnellement cher parce que la puissance augmente fortement quand on pousse tensions et activité de commutation. En termes simplifiés, la puissance dynamique suit à peu près :
Donc les derniers 10–20 % de fréquence peuvent demander un saut beaucoup plus important en watts — entraînant des limites thermiques et du throttling plutôt que des gains soutenus.
C'est pourquoi les conceptions modernes mettent l'accent sur l'efficacité : plus grand recours au parallélisme, gestion d'énergie intelligente, et fréquences « suffisantes » couplées à une meilleure microarchitecture. Dans les centres de données, l'énergie est un poste qui rivalise avec le coût matériel à long terme. Dans le cloud, un code inefficace peut augmenter directement la facture — car vous payez pour le temps, les cœurs, et souvent indirectement pour l'énergie.
Le point récurrent de Hennessy est simple : la mise à l'échelle des performances n'est pas seulement un problème matériel ou logiciel. La co-conception matériel–logiciel signifie aligner les caractéristiques CPU, les compilateurs, les runtimes et les algorithmes autour des workloads réels — pour que le système s'accélère sur ce que vous exécutez réellement, et non sur ce qui a l'air bien sur une fiche technique.
Un exemple classique est le support du compilateur qui débloque des capacités matérielles. Un processeur peut disposer d'unités vectorielles larges (SIMD), de prédiction de branche, ou d'instructions combinées, mais le logiciel doit être structuré pour que le compilateur puisse les utiliser.
Si le goulot est la latence mémoire, la contention de verrous ou l'E/S, une fréquence plus élevée ou plus de cœurs peut à peine bouger l'aiguille. Le système atteint simplement la même limite plus vite. Sans changements logiciels — meilleure structure parallèle, moins de misses de cache, moins de synchronisation — le nouveau matériel peut rester inactif.
Quand vous envisagez une optimisation ou une nouvelle plateforme, demandez-vous :
RISC (Reduced Instruction Set Computing) est moins un slogan qu'un pari stratégique : si vous maintenez un jeu d'instructions petit et régulier, vous pouvez faire exécuter chaque instruction rapidement et de façon prévisible. John Hennessy a aidé à populariser cette approche en soutenant que la performance s'améliore souvent quand le travail matériel est plus simple, même si le logiciel utilise plus d'instructions au total.
Un jeu d'instructions épuré tend à avoir des formats cohérents et des opérations simples (load, store, add, branch). Cette régularité facilite au CPU :
L'idée clé est que lorsque les instructions sont faciles à gérer, le processeur peut passer plus de temps à faire du travail utile et moins de temps à gérer des exceptions et des cas particuliers.
Les instructions complexes peuvent réduire le nombre d'instructions nécessaire au programme, mais elles augmentent souvent la complexité matérielle — plus de circuits, plus de cas limites, plus d'énergie dépensée en logique de contrôle. RISC inverse cela : utiliser des blocs simples, puis s'appuyer sur les compilateurs et la microarchitecture pour extraire la vitesse.
Cela peut se traduire par une meilleure efficacité énergétique. Une conception qui gaspille moins de cycles en overhead gaspille souvent aussi moins de joules, ce qui compte quand la puissance et la chaleur limitent la vitesse soutenue d'une puce.
Les CPU modernes — que ce soit dans les téléphones, les ordinateurs portables ou les serveurs — empruntent largement aux principes RISC : pipelines d'exécution réguliers, nombreuses optimisations autour d'opérations simples, et forte dépendance aux compilateurs. Les systèmes ARM sont un exemple visible de cette filiation RISC dans l'informatique grand public, mais la leçon n'est pas « quelle marque gagne ». Le principe durable est : choisir la simplicité quand elle permet un meilleur débit, une meilleure efficacité et une montée en échelle plus facile.
La spécialisation consiste à utiliser du matériel conçu pour faire une classe de travail extrêmement bien, au lieu de demander à un CPU généraliste de tout faire. Exemples courants : GPU pour le graphisme et le calcul parallèle, accélérateurs IA (NPUs/TPUs) pour les opérations matricielles, et blocs dédiés comme les encodeurs vidéo pour H.264/HEVC/AV1.
Un CPU est conçu pour la flexibilité : beaucoup d'instructions, beaucoup de logique de contrôle, et un bon traitement du code « branchy ». Les accélérateurs échangent cette flexibilité contre de l'efficacité. Ils consacrent plus de budget silicium aux opérations réellement nécessaires (par ex. multiply–accumulate), minimisent l'overhead de contrôle et utilisent souvent une moindre précision (INT8 ou FP16) quand la précision le permet.
Cette focalisation signifie plus de travail par watt : moins d'instructions, moins de mouvement de données, et plus d'exécution parallèle. Pour des workloads dominés par un noyau répétitif — rendu, inférence, encodage — cela peut produire des accélérations spectaculaires tout en maintenant la consommation maîtrisée.
La spécialisation a un coût. Vous pouvez perdre en flexibilité (le matériel excelle dans une tâche et est médiocre pour d'autres), payer des coûts d'ingénierie et de validation plus élevés, et dépendre d'un écosystème logiciel — pilotes, compilateurs, bibliothèques — qui peut être en retard ou vous enfermer chez un fournisseur.
Choisissez un accélérateur quand :
Restez sur CPU quand le workload est irrégulier, en évolution rapide, ou quand le coût logiciel dépasse les économies.
Chaque "gain" en architecture des ordinateurs a une facture. Le travail de Hennessy revient sans cesse à une vérité pratique : optimiser un système, c'est choisir ce à quoi on est prêt à renoncer.
Quelques tensions reviennent souvent :
Latence vs débit : on peut rendre une requête plus rapide (latence plus faible), ou augmenter le nombre de requêtes traitées par seconde (débit). Un CPU optimisé pour l'interactivité peut sembler plus réactif, tandis qu'une conception pour le batch vise le travail total accompli.
Simplicité vs fonctionnalités : les conceptions simples sont souvent plus faciles à optimiser, vérifier et étendre. Les conceptions riches en fonctionnalités peuvent aider certains workloads, mais elles ajoutent de la complexité qui peut ralentir le cas courant.
Coût vs vitesse : le matériel plus rapide coûte généralement plus — plus de surface silicium, plus de bande passante mémoire, plus de refroidissement, plus de temps d'ingénierie. Parfois l'accélération la moins chère est de changer le logiciel ou le workload.
Il est facile d'optimiser pour un seul chiffre et de dégrader l'expérience réelle. Par exemple, augmenter la fréquence peut accroître puissance et chaleur, provoquant du throttling qui nuit à la performance soutenue. Ajouter des cœurs peut améliorer le débit parallèle, mais augmenter la contention mémoire, rendant chaque cœur moins efficace. Un cache plus grand peut réduire les misses (bon pour la latence) tout en augmentant la surface silicium et l'énergie par accès (mauvais pour le coût et l'efficacité).
La perspective de Hennessy est pragmatique : définissez le workload qui compte, puis optimisez pour cette réalité.
Un serveur traitant des millions de requêtes similaires se préoccupe de débit prévisible et d'énergie par opération. Un portable se soucie de réactivité et d'autonomie. Un pipeline de données peut accepter une latence plus élevée si le temps total de job s'améliore. Les benchmarks et les specs headline sont utiles, mais seulement s'ils correspondent à votre cas d'utilisation.
Envisagez d'ajouter un petit tableau avec des colonnes : Décision, Aide, Désavantage, Idéal pour. Lignes possibles : « plus de cœurs », « cache plus grand », « fréquence plus élevée », « unités vectorielles plus larges », « mémoire plus rapide ». Cela rend les compromis concrets et maintient la discussion axée sur les résultats plutôt que sur le battage médiatique.
Les affirmations de performance valent ce que valent les mesures qui les soutiennent. Un benchmark peut être techniquement « correct » et trompeur s'il ne ressemble pas à votre workload réel : tailles de données, comportement de cache, schémas E/S, concurrence, ou mixe lectures/écritures peuvent renverser un résultat. C'est pourquoi les architectes à la manière de Hennessy traitent le benchmarking comme une expérience, pas comme un trophée.
Débit : combien de travail vous terminez par unité de temps (requêtes/s, jobs/heure). Utile pour la capacité, mais les utilisateurs ne ressentent pas les moyennes.
Latence de queue (tail latency) : se concentre sur les requêtes les plus lentes — souvent p95/p99. Un système peut avoir une excellente latence moyenne alors que p99 est catastrophique à cause de files, pauses GC, contention de verrous, ou voisins bruyants.
Utilisation : à quel point une ressource est « occupée » (CPU, bande passante mémoire, disque, réseau). Une utilisation élevée peut être bonne — jusqu'à provoquer de longues files et faire exploser la latence au tail.
Utilisez une boucle reproductible :
Conservez des notes sur la configuration, les versions et l'environnement pour reproduire les résultats plus tard.
Ne sélectionnez pas la « meilleure exécution », le jeu de données le plus favorable, ou une métrique unique flatteuse. Ne généralisez pas : un gain sur une machine ou un benchmark peut ne pas tenir pour votre déploiement, vos contraintes de coût, ou le trafic pic des utilisateurs.
Le message durable de Hennessy est pratique : la performance ne se met pas à l'échelle par souhait — elle s'obtient quand vous choisissez le bon type de parallélisme, respectez les limites d'énergie, et optimisez pour les workloads qui comptent.
Le parallélisme est la principale voie d'avenir, mais il n'est jamais « gratuit ». Qu'il s'agisse d'ILP, de débit multicœur ou d'accélérateurs, les gains faciles s'épuisent et les frais de coordination augmentent.
L'efficacité est une fonctionnalité. L'énergie, la chaleur et le mouvement des données mémoire limitent souvent la vitesse réelle bien avant les chiffres de pointe en GHz. Une conception plus rapide qui ne peut pas rester dans les limites de puissance ou mémoire n'apportera pas de gains visibles pour l'utilisateur.
La focalisation sur le workload bat l'optimisation générique. La loi d'Amdahl rappelle d'investir où le temps est passé. Profilage d'abord ; optimisation ensuite.
Ces idées ne sont pas réservées aux concepteurs de CPU. Si vous construisez une application, les mêmes contraintes se manifestent comme files d'attente, latence tail, pression mémoire et coût cloud. Une façon pratique d'opérationnaliser la « co-conception » est de garder les décisions d'architecture proches du feedback sur les workloads : mesurer, itérer et déployer.
Pour les équipes utilisant un workflow orienté chat comme Koder.ai, cela peut être particulièrement utile : vous pouvez prototyper rapidement un service ou une UI, puis utiliser le profilage et les benchmarks pour décider s'il faut poursuivre le parallélisme (p. ex. concurrence de requêtes), améliorer la localité des données (p. ex. moins d'allers-retours, requêtes plus ciblées), ou introduire de la spécialisation (p. ex. déléguer des tâches lourdes). Les fonctionnalités de la plateforme — mode de planification, instantanés et restauration — facilitent les tests incrémentaux des changements qui impactent la performance, sans transformer l'optimisation en une voie sans retour.
Si vous voulez d'autres articles comme celui-ci, parcourez /blog.