8 min

L'état d'esprit performance de John Carmack pour les graphismes en temps réel

Un guide pratique sur la mentalité « performance d’abord » associée à John Carmack : profilage, budgets de temps par frame, arbitrages et mise en production de systèmes temps réel complexes.

L'état d'esprit performance de John Carmack pour les graphismes en temps réel

Pourquoi l'approche de Carmack compte encore

John Carmack est souvent traité comme une légende des moteurs de jeu, mais la partie utile n’est pas la mythologie — ce sont les habitudes répétables. Il ne s’agit pas d’imiter le style d’une personne ou de supposer des « coups de génie ». Il s’agit de principes pratiques qui mènent de façon fiable à des logiciels plus rapides et plus fluides, surtout quand les délais et la complexité s’accumulent.

Ingénierie de la performance, en termes simples

L'ingénierie de la performance consiste à faire en sorte que le logiciel atteigne une cible de vitesse sur du matériel réel, dans des conditions réelles — sans casser la correction. Ce n’est pas « le rendre vite à tout prix ». C’est une boucle disciplinée :

  • décider ce que « assez rapide » signifie
  • mesurer où le temps est réellement passé
  • changer une chose volontairement
  • vérifier que vous avez amélioré la bonne métrique

Cet état d’esprit revient sans cesse dans le travail de Carmack : discuter avec des données, garder les changements explicables et préférer des approches maintenables.

Pourquoi les graphismes temps réel exposent la réalité

Les graphismes temps réel ne pardonnent pas parce qu’il y a une deadline à chaque frame. Si vous la manquez, l’utilisateur le ressent immédiatement sous forme de saccades, latence d’entrée ou mouvement irrégulier. D’autres logiciels peuvent cacher l’inefficacité derrière des files d’attente, écrans de chargement ou tâches en arrière-plan. Un renderer ne peut pas négocier : soit vous finissez à temps, soit vous ne finissez pas.

C’est pourquoi les leçons généralisent au-delà des jeux. Tout système avec des contraintes de latence serrées — UI, audio, AR/VR, trading, robotique — bénéficie d’une pensée en budgets, d’une compréhension des goulots et d’une prévention des pics surprises.

Ce que vous en tirerez

Vous obtiendrez des checklists, heuristiques et schémas de décision applicables à votre travail : comment définir des budgets de temps par frame (ou de latence), comment profiler avant d’optimiser, comment choisir la « seule chose » à corriger, et comment prévenir les régressions pour que la performance devienne une routine — pas une panique de dernière minute.

Penser en budgets de temps par frame, pas en sensation

La pensée de performance à la Carmack commence par un simple changement : arrêtez de parler du « FPS » comme unité primaire et commencez à parler du temps par frame.

Le FPS est réciproque (« 60 FPS » sonne bien, « 55 FPS » paraît proche), mais l’expérience utilisateur est guidée par la durée de chaque frame — et, tout aussi important, par la constance de ces durées. Un saut de 16,6 ms à 33,3 ms est immédiatement visible, même si votre FPS moyen semble encore respectable.

Temps par frame vs FPS (pourquoi le temps par frame gagne)

  • Le FPS masque la variabilité. Deux builds peuvent « moyenner 60 FPS », mais l’un peut saccader à cause d’images occasionnelles à 40–60 ms.
  • Le temps par frame se mappe au travail. Chaque milliseconde est une tranche réelle de travail CPU/GPU que vous pouvez attribuer aux systèmes.
  • Les objectifs sont plus clairs. « Restez sous 16,6 ms » est une exigence concrète ; « donner une sensation de fluidité » ne l’est pas.

Budgets : ce que vous dépensez vraiment

Un produit temps réel a plusieurs budgets, pas seulement « rendre plus vite » :

  • Temps CPU (logique de jeu, animation, culling, soumission des draw calls)
  • Temps GPU (shading, post-traitement, overdraw, résolution)
  • Mémoire (empreinte, pics, fragmentation, marge pour le streaming)
  • Temps de chargement (démarrage, chargement de niveaux, compilation de shaders, stalls de streaming)

Ces budgets interagissent. Économiser du temps GPU en ajoutant du batching gourmand en CPU peut se retourner contre vous, et réduire la mémoire peut augmenter les coûts de streaming ou de décompression.

Exemple : 16,6 ms à 60 FPS

Si votre cible est 60 FPS, votre budget total est 16,6 ms par frame. Une répartition approximative pourrait ressembler à :

  • CPU : 7 ms (simulation, gameplay, visibilité)
  • GPU : 9 ms (rendu + post)
  • OS/driver + tampon d’overhead : ~0,6 ms

Si le CPU ou le GPU dépasse le budget, vous ratez la frame. C’est pourquoi les équipes parlent d’être « CPU-bound » ou « GPU-bound » — pas comme des étiquettes, mais comme un moyen de décider d’où proviendra le prochain milliseconde réaliste.

« Assez rapide » est une exigence produit

Le but n’est pas de courir après un metric de vanité comme « le plus haut FPS sur une machine haut de gamme ». Il s’agit de définir ce qui est assez rapide pour votre audience — cibles matérielles, résolution, autonomie, contraintes thermiques et réactivité d’entrée — puis de traiter la performance comme des budgets explicites que vous pouvez gérer et défendre.

Profilage d'abord : mesurer, puis décider

Le mouvement par défaut de Carmack n’est pas « optimiser », c’est « vérifier ». Les problèmes de performance temps réel foisonnent d’explications plausibles — pauses GC, « shaders lents », « trop de draw calls » — et la plupart sont fausses dans votre build sur votre matériel. Le profilage est la façon de remplacer l’intuition par des preuves.

Commencez par mesurer (avant de deviner)

Traitez le profilage comme une fonctionnalité de première classe, pas comme un outil de sauvetage de dernière minute. Capturez les temps par frame, les timelines CPU et GPU, et les compteurs qui les expliquent (triangles, draw calls, changements d’état, allocations, cache misses si vous pouvez les obtenir). L’objectif est de répondre à une question : où le temps est-il réellement passé ?

Un modèle utile : dans chaque frame lente, une chose est le facteur limitant. Peut-être le GPU bloqué sur une passe lourde, le CPU bloqué sur la mise à jour d’animations, ou le thread principal en attente d’une synchronisation. Trouvez cette contrainte d’abord ; le reste n’est que bruit.

Itérer comme un scientifique

Une boucle disciplinée vous empêche de vous emballer :

  • Mesurez une baseline avec une scène et un chemin de caméra reproductibles
  • Changez une chose
  • Re-mesurez, et notez le delta

Si l’amélioration n’est pas claire, supposez qu’elle n’a pas aidé — car elle ne survivra probablement pas aux prochains ajouts de contenu.

Méfiez-vous des optimisations placebo

Le travail de performance est particulièrement vulnérable à l’auto-illusion :

  • Erreurs de benchmarking : scènes de test inconsistantes, builds de debug, tâches en arrière-plan, throttling thermique, différences de vsync
  • Biais de confirmation : « ça a l’air plus rapide » sans données de temps par frame
  • Moyennes trompeuses : une meilleure moyenne peut cacher des pics plus mauvais

Le profilage en premier garde vos efforts ciblés, vos arbitrages justifiés et vos changements plus faciles à défendre en revue.

Goulots : trouvez la seule chose qui est vraiment lente

Les problèmes de performance temps réel semblent désordonnés parce que tout se passe en même temps : gameplay, rendu, streaming, animation, UI, physique. L’instinct de Carmack est de couper à travers le bruit et d’identifier le limitant dominant — la seule chose qui fixe actuellement votre temps par frame.

Catégories courantes de goulots

La plupart des ralentissements tombent dans quelques bacs :

  • CPU-bound : le thread principal (ou un worker critique) ne peut pas finir son travail à temps — logique de jeu, soumission des draw calls, physique, évaluation d’animation.
  • GPU-bound : le GPU ne peut pas finir la frame — shaders lourds, trop de pixels, post-traitements coûteux, géométrie complexe.
  • Memory-bound : vous êtes limité par la bande passante/la latence — cache misses, mauvais layout de données, accès aléatoires, copies de gros buffers.
  • I/O-bound : streaming d’assets, compilation de shaders, décompression, lectures fichiers, attentes réseau.

L’objectif n’est pas d’étiqueter pour un rapport — c’est de choisir le bon levier.

Manières rapides de diagnostiquer (avant de réécrire quoi que ce soit)

Quelques expériences rapides peuvent indiquer ce qui est réellement en contrôle :

  • Test d’échelle de résolution : baissez la résolution de rendu (ou forcez la résolution dynamique). Si le temps par frame s’améliore beaucoup, vous êtes probablement limité GPU/pixel. Si ça ne bouge presque pas, regardez le CPU ou le travail GPU non-pixel.
  • Désactivation de fonctionnalités : éteignez ombres, SSR, AO, particules ou passes coûteuses une à une. Un changement significatif révèle où va le temps.
  • Instrumentation et captures : utilisez des timers embarqués, un profileur CPU et une capture GPU pour voir où atterrissent réellement les millisecondes.

Principe du « gros caillou »

Vous gagnez rarement en enlevant 1 % à dix systèmes. Trouvez le coût le plus important qui se répète chaque frame et attaquez-le en premier. Supprimer une seule offense de 4 ms bat des semaines de micro-optimisations.

Les goulots se déplacent

Après avoir corrigé le gros caillou, le prochain gros caillou devient visible. C’est normal. Traitez le travail de performance comme une boucle : mesurer → changer → re-mesurer → re-prioriser. Le but n’est pas un profil parfait ; c’est un progrès régulier vers un temps par frame prévisible.

La fluidité gagne : pics, saccades et latence en queue

Le temps par frame moyen peut sembler correct alors que l’expérience reste mauvaise. Les graphismes temps réel sont jugés par les pires moments : la frame manquée pendant une grosse explosion, le hitch en entrant dans une nouvelle salle, la saccade soudaine à l’ouverture d’un menu. C’est la latence en queue — des frames rares mais suffisamment fréquentes pour que les utilisateurs les remarquent.

Pourquoi les queues comptent plus que les moyennes

Un jeu qui tourne à 16,6 ms la plupart du temps (60 FPS) mais pique à 60–120 ms toutes les quelques secondes paraîtra « cassé », même si la moyenne s’affiche encore à 20 ms. Les humains sont sensibles au rythme. Une seule longue frame casse la prédictibilité des entrées, le mouvement de la caméra et la synchro audio/vidéo.

Sources courantes de pics

Les pics viennent souvent de travaux qui ne sont pas répartis de façon homogène :

  • Garbage collection ou page faults mémoire qui mettent le monde en pause
  • Compilation de shaders et création de pipelines déclenchées « à la volée »
  • Streaming d’assets qui nécessite soudainement décompression, uploads ou I/O fichiers
  • Ordonnancement OS et tâches background qui volent du CPU (ou changements de fréquence/thermique)

Stratégies pour réduire les saccades

L’objectif est de rendre le travail coûteux prévisible :

  • Précomputez ce que vous pouvez : compiler les shaders hors ligne, baker des données, préparer des tables de recherche.
  • Warm up tôt : compiler les shaders, créer les pipelines, toucher les assets critiques pendant les écrans de chargement ou une scène de chauffe contrôlée.
  • Amortissez les tâches lourdes : répartissez le streaming, la décompression et les uploads sur plusieurs frames plutôt que tout d’un coup.
  • Limitez le travail par frame : appliquez des budgets temporels (ex. « pas plus de 2 ms pour le streaming cette frame ») et différer le reste.

Logger et visualiser la queue

Ne vous contentez pas d’un graphique de FPS moyen. Enregistrez les timings par frame et visualisez :

  • Histogrammes du temps par frame pour voir regroupements et outliers
  • Percentiles (p95, p99, p99.9) pour suivre explicitement la queue
  • Marqueurs de spikes avec événements corrélés (début GC, compilation de shader, chargement d’asset)

Si vous ne pouvez pas expliquer vos 1 % pires frames, vous n’avez pas vraiment expliqué la performance.

Rendre les arbitrages explicites (Qualité vs Vitesse vs Complexité)

Corréler les pics à leurs causes
Enregistrez les pics avec des tags d'événement comme GC, streaming et compilation de shaders pour un diagnostic rapide.

Le travail de performance devient plus simple dès que vous cessez de prétendre tout pouvoir avoir. Le style de Carmack pousse les équipes à nommer l’arbitrage à voix haute : qu’achetons-nous, qu’en payons-nous et qui en ressent la différence ?

Nommez les axes (et le coût réel)

La plupart des décisions se situent sur quelques axes :

  • Qualité : fidélité visuelle, précision de simulation, ressenti des entrées
  • Vitesse : temps par frame, temps de chargement, temps de compilation, temps d’itération
  • Mémoire : VRAM, RAM, bande passante
  • Complexité : debugging plus difficile, plus de cas limites, plus de charges de test
  • Temps de mise sur le marché : risque sur le planning, risque d’intégration, focussation de l’équipe

Si un changement améliore un axe mais en taxe trois autres silencieusement, documentez-le. « Ceci ajoute 0,4 ms GPU et 80 Mo VRAM pour des ombres plus douces » est une phrase utile. « Ça a l’air mieux » ne l’est pas.

Définir des seuils « assez bon »

Les graphismes temps réel ne cherchent pas la perfection ; ils visent à toucher une cible de manière cohérente. Mettez-vous d’accord sur des seuils comme :

  • FPS minimal / temps par frame maximal sur une machine de référence
  • pics acceptables (pas seulement la moyenne)
  • plafonds mémoire par plateforme

Une fois l’équipe d’accord que, disons, 16,6 ms à 1080p sur le GPU de base est l’objectif, les discussions deviennent concrètes : cette fonctionnalité nous garde-t-elle sous le budget, ou force-t-elle une dégradation ailleurs ?

Préférer les décisions réversibles

Quand vous n’êtes pas sûr, choisissez des options que vous pouvez annuler :

  • feature flags pour les effets risqués
  • réglages scalables (low/medium/high) qui correspondent à des coûts réels
  • chemins de repli pour le matériel plus ancien

La réversibilité protège le planning. Vous pouvez livrer le chemin sûr et garder l’ambitieux derrière un toggle.

Optimisez ce que les utilisateurs peuvent ressentir

Évitez de sur-ingéniéer des gains invisibles. Une amélioration de 1 % de la moyenne vaut rarement un mois de complexité — sauf si elle élimine des saccades, corrige la latence d’entrée ou prévient un crash mémoire dur. Priorisez les changements que les joueurs remarquent immédiatement, et laissez le reste attendre.

Discipline d’ingénierie : la correction permet la vitesse

Le travail de performance devient beaucoup plus simple quand le programme est juste. Une grande part du temps passé à « optimiser » sert en réalité à traquer des bugs de correction qui ressemblent à des problèmes de performance : une boucle O(N²) accidentelle à cause d’un travail dupliqué, une passe de rendu qui s’exécute deux fois parce qu’un flag ne se réinitialise pas, une fuite mémoire qui augmente doucement le temps par frame, ou une condition de course qui tourne en saccade aléatoire.

Traitez la correction comme un outil de performance

Un moteur stable et prévisible vous donne des mesures propres. Si le comportement change entre les runs, vous ne pouvez pas faire confiance aux profils, et vous finirez par optimiser du bruit.

Les pratiques d’ingénierie disciplinées aident la vitesse :

  • Invariants clairs : définissez ce qui doit toujours être vrai (par ex. « chaque objet visible est soumis une fois », « les ressources GPU ne sont pas mutées en vol », « le frame graph n’a pas de cycles »).
  • Validation en builds debug : ajoutez assertions et vérifications légères qui hurlent tôt—avant qu’un état cassé ne devienne un hitch mystérieux. Validez les tailles de buffers, les transitions d’état et que les allocations par frame restent sous une limite connue.

Rendre les bugs de performance reproductibles à la demande

Beaucoup de pics de temps par frame sont des « Heisenbugs » : ils disparaissent quand vous ajoutez du logging ou que vous attachez un débogueur. L’antidote est la reproduction déterministe.

Construisez un petit banc d’essai contrôlé :

  • Scènes de test minimales qui isolent une fonctionnalité (ombrage, particules, UI, streaming)
  • Chemins de caméra fixes et entrées scriptées pour que chaque run soit comparable
  • Paramètres verrouillés (résolution, niveau de qualité, pas de temps fixe quand c’est possible) pour éliminer les variables

Quand un hitch apparaît, vous voulez un bouton qui le rejoue 100 fois — pas un rapport vague disant que ça « arrive parfois après 10 minutes ».

Changer moins, apprendre plus

Le travail de performance bénéficie de changements petits et revus. Les gros refactors créent plusieurs modes de défaillance à la fois : régressions, nouvelles allocations et travail caché en plus. Des diffs serrés facilitent la réponse à la seule question qui compte : qu’est-ce qui a changé dans le temps par frame, et pourquoi ?

La discipline n’est pas de la bureaucratie ici — c’est la façon de garder les mesures fiables pour que l’optimisation devienne directe au lieu d’être superstitieuse.

Travailler avec la machine : données, cache et overhead

Partagez une source unique de vérité
Hébergez vos outils internes pour que toute l'équipe utilise les mêmes chiffres.

La performance temps réel ne se résume pas à du « code plus rapide ». Il s’agit d’organiser le travail pour que le CPU et le GPU puissent l’exécuter efficacement. Carmack a souvent souligné une vérité simple : la machine est littérale. Elle adore les données prévisibles et déteste les surcoûts évitables.

Pensée orientée données : faciliter la lecture mémoire

Les CPUs modernes sont incroyablement rapides — jusqu’à ce qu’ils attendent la mémoire. Si vos données sont dispersées en beaucoup de petits objets, le CPU passe du temps à suivre des pointeurs au lieu de faire des calculs.

Un modèle mental utile : ne faites pas dix petits trajets pour dix articles. Mettez-les dans un seul chariot et parcourez les rayons une fois. En code, cela signifie garder les valeurs fréquemment utilisées proches (souvent en tableaux ou structs compactes) pour que chaque fetch de cache line apporte des données que vous allez vraiment utiliser.

Schémas d’allocation : le petit churn devient une grosse douleur

Les allocations fréquentes créent des coûts cachés : overhead de l’allocateur, fragmentation mémoire et pauses imprévisibles quand le système doit ranger. Même si chaque allocation est « petite », un flux régulier peut devenir une taxe que vous payez chaque frame.

Les corrections courantes sont volontairement ennuyeuses : réutiliser les buffers, pooler les objets et préférer des allocations longue durée pour les chemins chauds. L’objectif n’est pas la ruse — c’est la cohérence.

Batching : réduire l’overhead avant d’optimiser les maths

Une quantité surprenante de temps par frame peut s’évaporer dans la paperasserie : changements d’état, draw calls, travail du driver, syscalls et coordination de threads.

Le batching est la version « gros chariot » du rendu et de la simulation. Au lieu d’émettre beaucoup de petites opérations, groupez le travail similaire pour franchir moins souvent des frontières coûteuses. Souvent, réduire l’overhead bat l’optimisation micro d’un shader ou d’une boucle interne — parce que la machine passe moins de temps à se préparer et plus de temps à travailler.

Simplicité comme stratégie de performance

Le travail de performance n’est pas seulement du code plus rapide — c’est aussi avoir moins de code. La complexité a un coût que vous payez chaque jour : les bugs prennent plus de temps à isoler, les correctifs exigent plus de tests, l’itération ralentit parce que chaque changement touche plus de pièces, et les régressions s’infiltrent via des chemins peu utilisés. Cette complexité ne gaspille pas seulement le temps des développeurs ; elle ajoute souvent un overhead runtime (branches supplémentaires, allocations, cache misses, synchronisations) difficile à voir avant qu’il ne soit trop tard.

La taxe cachée de la complexité

Un système « clever » peut sembler élégant jusqu’à ce que vous soyez sous pression et qu’un pic n’apparaisse que sur une carte, un GPU ou une combinaison de paramètres. Chaque drapeau de fonctionnalité, chemin de repli et cas spécial multiplie le nombre de comportements à comprendre et mesurer. Cette complexité n’est pas seulement du temps dev ; elle ajoute souvent un surcoût runtime difficile à débusquer.

Préférez des solutions explicables

Bonne règle : si vous ne pouvez pas expliquer le modèle de performance à un coéquipier en quelques phrases, vous ne pouvez probablement pas l’optimiser de manière fiable.

Les solutions simples ont deux avantages :

  • Elles sont plus faciles à profiler et raisonner (moins de variables)
  • Elles réduisent les « inconnues inconnues », où un tweak mineur cause des ralentissements inattendus

« Supprimer du code » est un vrai outil d’optimisation

Parfois le chemin le plus rapide est de supprimer une fonctionnalité, couper une option ou fusionner plusieurs variantes en une seule. Moins de fonctionnalités signifie moins de chemins de code, moins de combinaisons d’état et moins d’endroits où la performance peut se dégrader en silence.

Supprimer du code est aussi un geste de qualité : le meilleur bug est celui que vous empêchez en supprimant le module qui pouvait le générer.

Refactor ou patch ? Checklist de décision rapide

Patch (correctif chirurgical) quand :

  • vous avez identifié un hot path spécifique et un petit changement l’améliore de façon mesurable
  • le système est stable et largement utilisé ; changer l’architecture risque de nouveaux régressions
  • vous avez besoin d’une amélioration sûre qui tient dans le calendrier de release

Refactorer (simplifier la structure) quand :

  • le profil pointe un overhead réparti sur de nombreux sites ou couches
  • vous rouvrez souvent la performance dans la même zone après des changements non liés
  • le code demande du tribal knowledge pour être modifié en sécurité
  • vous pouvez supprimer ou fusionner des chemins et finir avec moins de concepts au total

La simplicité n’est pas « moins ambitieux ». C’est choisir des designs qui restent compréhensibles sous pression — quand la performance compte le plus.

Prévenir les régressions : faire de la performance une habitude

Le travail de performance ne tient que si vous pouvez détecter quand il glisse. Voilà ce qu’est un test de régression de performance : un moyen reproductible de détecter quand un changement rend le produit plus lent, moins fluide ou plus gourmand en mémoire. Contrairement aux tests fonctionnels (qui répondent à « ça marche ? »), les tests de régression répondent à « est-ce que ça reste aussi rapide ? » Un build peut être 100 % correct et rester une mauvaise release s’il ajoute 4 ms de temps par frame ou double les temps de chargement.

Un workflow léger qui est réellement utilisé

Vous n’avez pas besoin d’un labo pour commencer — juste de la constance.

Choisissez un petit ensemble de scènes de référence représentant un usage réel : une vue GPU-heavy, une vue CPU-heavy et une scène de stress "pire cas". Gardez-les stables et scriptées pour que le chemin de caméra et les entrées soient identiques à chaque run.

Exécutez les tests sur matériel fixe (PC/console/devkit connu). Si vous changez drivers, OS ou réglages d’horloge, enregistrez-le. Traitez la combinaison matériel/logiciel comme faisant partie du dispositif de test.

Stockez les résultats dans un historique versionné : hash de commit, config de build, ID machine et métriques mesurées. Le but n’est pas un nombre parfait — c’est une courbe de tendance fiable.

Métriques CI-friendly à suivre

Privilégiez des métriques difficiles à contester :

  • Percentiles de temps par frame (p50/p95/p99), pas seulement le FPS moyen. Les percentiles font ressortir la saccade et les queues.
  • Mémoire max (et pics d’allocation). La dérive mémoire apparaît souvent avant les crashs.
  • Temps de chargement (cold start et transitions de scènes/niveaux), car les joueurs remarquent des secondes de plus plus que des micro-optimisations.

Définissez des seuils simples (par ex. : p95 ne doit pas régresser de plus de 5 %).

Que faire quand vous attrapez une régression

Traitez les régressions comme des bugs avec un propriétaire et une deadline.

D’abord, bisectez pour trouver le changement qui l’a introduite. Si la régression bloque une release, revertissez rapidement et relevez avec un correctif.

Quand vous corrigez, ajoutez des garde-fous : gardez le test, ajoutez une note dans le code et documentez le budget attendu. L’habitude est la victoire — la performance devient quelque chose que vous maintenez, pas que vous « ferez plus tard ».

Livrer des systèmes complexes : performance, délais et réalité

Automatisez vos références de performance
Lancez un exécuteur de benchmark répétable avec un backend Go et une interface de résultats claire.

« Livrer » n’est pas un événement calendaire — c’est une exigence d’ingénierie. Un système qui ne marche bien qu’en labo, ou qui n’atteint le temps par frame qu’après une semaine d’ajustements manuels, n’est pas prêt. L’état d’esprit de Carmack traite les contraintes du monde réel (variété matérielle, contenu désordonné, comportement joueur imprévisible) comme faisant partie du cahier des charges dès le départ.

Livrer signifie choisir ce qui doit être vrai

Quand vous êtes proche de la release, la prévisibilité vaut mieux que la perfection. Définissez les non-négociables en termes clairs : FPS cible, pics de temps par frame acceptables, limites mémoire et temps de chargement. Traitez tout ce qui les viole comme un bug, pas comme du « polish ». Cela re-cadre le travail de performance de l’optimisation optionnelle en travail de fiabilité.

Priorisez ce que les joueurs ressentent réellement

Toutes les lenteurs ne se valent pas. Corrigez d’abord les problèmes visibles par l’utilisateur :

  • Les saccades et grands pics surpassent souvent un rendu légèrement plus lent en qualité perçue.
  • Les hitchs de menu, les pops de streaming et la latence d’entrée nuisent plus souvent à l’expérience qu’une petite perte de FPS moyen.
  • Les régressions dans des scénarios communs (combat chargé, rotations de caméra, moments riches en effets) méritent la priorité sur des cas rares.

La discipline du profilage paye ici : vous ne devinez pas quel problème « semble gros », vous choisissez sur la base de l’impact mesuré.

Étalez les changements et par défaut soyez conservateur

Le travail de performance en fin de cycle est risqué car les « fixes » peuvent introduire de nouveaux coûts. Utilisez des déploiements par étapes : landez l’instrumentation d’abord, puis le changement derrière un toggle, puis étendez l’exposition. Préférez des valeurs par défaut sûres — réglages qui protègent le temps par frame même si elles réduisent un peu la qualité visuelle — surtout pour les configurations auto-détectées.

Si vous expédiez sur plusieurs plateformes ou niveaux, traitez les defaults comme une décision produit : mieux vaut sembler un peu moins fancy que d’être instable.

Communiquez les contraintes aux non-techniques

Traduisez les arbitrages en résultats : « Cet effet coûte 2 ms par frame sur les GPU milieu de gamme, ce qui risque de nous faire tomber sous 60 FPS pendant les combats. » Proposez des options, pas des leçons : réduire la résolution, simplifier le shader, limiter le taux d’apparition, ou accepter une cible plus basse. Les contraintes sont plus faciles à accepter quand elles sont présentées comme des choix concrets avec un impact utilisateur clair.

Une checklist pratique pour appliquer l'état d'esprit aujourd'hui

Vous n’avez pas besoin d’un nouveau moteur ni d’une réécriture pour adopter la pensée performance à la Carmack. Il vous faut une boucle reproductible qui rend la performance visible, testable et difficile à casser accidentellement.

La boucle reproductible (mesurer → budgéter → isoler → optimiser → valider → documenter)

  1. Mesurer : capturez une baseline (moyenne, p95, pire spike) pour le temps par frame et les sous-systèmes clés.

  2. Budgéter : fixez un budget par frame pour le CPU et le GPU (et la mémoire si vous êtes serrés). Écrivez le budget à côté de l’objectif fonctionnalité.

  3. Isoler : reproduisez le coût dans une scène minimale ou un test. Si vous ne pouvez pas le reproduire, vous ne pouvez pas le corriger.

  4. Optimiser : changez une chose à la fois. Préférez des changements qui réduisent le travail, pas seulement « le rendre plus rapide ».

  5. Valider : re-profilez, comparez les deltas et vérifiez les régressions qualité et les problèmes de correction.

  6. Documenter : notez ce qui a changé, pourquoi cela a aidé et quoi surveiller à l’avenir.

Règles empiriques à appliquer immédiatement

  • Optimisez la barre la plus grosse, pas la supposition la plus agaçante.
  • Chassez les pics avant les moyennes si les utilisateurs ressentent des saccades.
  • Si vous ne pouvez pas expliquer le coût, vous ne maîtrisez pas encore la fonctionnalité.
  • Préférez des coûts prévisibles plutôt que des explosions rares en pire-cas.
  • Budgetez le travail nouveau en amont (ms CPU, ms GPU, mémoire, bande passante).
  • Évitez les boucles cachées par objet/par frame qui croissent avec le contenu.
  • Faites des tests de performance une partie du « done », pas une urgence de pré-release.

Un simple modèle de « revue performance » (avant merge)

  • Résumé de la fonctionnalité : ce qui a changé, ce que ça permet
  • Plateformes & réglages cibles : (ex. mode perf console, PC milieu de gamme)
  • Budget : CPU __ ms, GPU __ ms, mémoire __ MB
  • Baseline vs après : moy. / ms, p95 / ms, pire spike / ms
  • Hypothèse de goulot : CPU ou GPU ? preuves :
  • Scène de test & étapes pour reproduire :
  • Risques & garde-fous : ce qui pourrait régresser, quelles métriques alertent
  • Plan de rollback : comment désactiver ou dégrader proprement

Où Koder.ai s’insère dans ce workflow

Si vous voulez opérationnaliser ces habitudes au sein d’une équipe, la clé est de réduire la friction : expériences rapides, harnesses reproductibles et rollbacks faciles.

Koder.ai peut aider quand vous construisez les outils autour — pas le moteur lui-même. Étant une plateforme vibe-coding qui génère du code réel et exportable (apps web en React ; backends en Go avec PostgreSQL ; mobile en Flutter), vous pouvez rapidement monter des dashboards internes pour percentiles de temps par frame, historique de régressions et checklists de « revue performance », puis itérer via chat au fur et à mesure que les exigences évoluent. Les snapshots et rollback s’accordent aussi bien avec la boucle « changer une chose, re-mesurer ».

Si vous voulez plus de conseils pratiques, parcourez /blog ou voyez comment les équipes opérationnalisent cela sur /pricing.

FAQ

Pourquoi l'article met-il l'accent sur le temps par frame (ms) plutôt que sur le FPS ?

Le temps par frame est le temps par image en millisecondes (ms) et correspond directement à la quantité de travail faite par le CPU/GPU.

  • Le FPS est réciproque et peut masquer la variabilité.
  • Le temps par frame révèle les saccades (par ex. des frames occasionnels à 40–120 ms) même quand la moyenne de FPS semble correcte.
  • Il est plus simple à budgéter : 16,6 ms = 60 FPS, 33,3 ms = 30 FPS.
Comment définir un budget de temps par frame pratique pour mon projet ?

Choisissez une cible (par ex. 60 FPS) et convertissez-la en un délai strict (16,6 ms). Ensuite, répartissez ce délai en budgets explicites.

Exemple de point de départ :

  • CPU : ~7 ms
  • GPU : ~9 ms
  • Tampon de surcharge : ~0,6 ms

Considérez ces valeurs comme des exigences produit et ajustez-les selon la plateforme, la résolution, les contraintes thermiques et les objectifs de latence d'entrée.

Quel est l'équipement de profilage minimum à avoir avant d'optimiser ?

Commencez par rendre vos tests reproductibles, puis mesurez avant de changer quoi que ce soit.

  • Utilisez une scène fixe + chemin de caméra fixe
  • Capturez timeline CPU + timeline GPU
  • Enregistrez des compteurs d'accompagnement (draw calls, triangles, allocations, événements de streaming)

Ce n'est qu'après avoir su où le temps est dépensé que vous pouvez décider quoi optimiser.

Comment savoir rapidement si je suis limité par le CPU ou par le GPU ?

Exécutez des tests courts et ciblés qui isolent le limiteur :

  • Baisser la résolution : une grosse amélioration indique généralement une limitation GPU / pixel.
  • Désactiver des fonctions une par une (ombres, SSR, AO, particules) : celle qui réduit nettement le temps par frame est souvent le « gros caillou » actuel.
  • Confirmer avec un profileur CPU et une capture GPU.

Évitez de réécrire des systèmes avant de pouvoir nommer le coût dominant en millisecondes.

Pourquoi les pics de temps par frame (latence en queue) sont-ils plus importants que le FPS moyen ?

Parce que les utilisateurs ressentent les pires frames, pas la moyenne.

Suivez :

  • Les percentiles (p95/p99/p99.9) pour exposer la latence en queue
  • Des histogrammes pour voir regroupements et valeurs aberrantes
  • La corrélation d'événements (GC, compilation de shader, chargement d'assets) pour attribuer les spikes

Un build qui moyenne 16,6 ms mais pique à 80 ms donnera quand même une sensation de mauvaise qualité.

Quelles sont des façons pratiques de réduire les saccades et les accrocs ?

Rendez le travail coûteux prévisible et planifié :

  • Précomputez ce que vous pouvez (builds de shaders hors ligne, données baked)
  • Warm up : compiler les shaders, créer les pipelines durant les écrans de chargement ou une scène de chauffe contrôlée
  • Amortissez le streaming / décompression / uploads sur plusieurs frames
  • Limitez le travail par frame (par ex. « le streaming a max 2 ms cette frame »)

Consignez aussi les spikes pour pouvoir les reproduire et les corriger, plutôt que d’espérer qu’ils disparaissent.

Comment décider entre qualité visuelle, performance et complexité ?

Formulez l'arbitrage explicitement en chiffres et en impact utilisateur.

Utilisez des énoncés comme :

  • « Cela ajoute 0,4 ms GPU et 80 Mo VRAM pour adoucir les ombres. »

Puis décidez selon des seuils convenus :

  • temps par frame max sur la machine de référence
  • pics acceptables
  • plafonds mémoire par plateforme

Si vous hésitez, favorisez des décisions réversibles (feature flags, paliers de qualité).

Pourquoi la correction (correctness) est-elle si importante pour le travail de performance ?

Parce qu'une correction instable rend les données de performance peu fiables.

Mesures pratiques :

  • Définir des invariants (p.ex., « chaque objet visible est soumis une fois »).
  • Ajouter des validations en debug (assertions, vérifications légères) qui échouent tôt—avant qu'un état cassé ne devienne un hitch mystérieux.
  • Construire des harnesses de repro déterministes (scènes minimales, entrées scriptées).

Si le comportement varie d'une exécution à l'autre, vous optimisez du bruit plutôt que des goulots réels.

Que signifie « travailler avec la machine » en pratique (cache, données, batching) ?

La plupart du « code rapide » consiste en réalité à réduire la mémoire et les surcoûts.

Concentrez-vous sur :

  • Localité des données : garder les données chaudes contiguës pour réduire les cache misses.
  • Contrôle des allocations : réutiliser les buffers, pooler les objets, éviter le churn par frame.
  • Batching : réduire draw calls / changements d'état / points de synchronisation avant d’optimiser des boucles internes.

Souvent, couper les frais généraux donne de plus grands gains que d'ajuster une petite boucle mathématique.

Comment prévenir les régressions de performance à mesure que le projet évolue ?

Rendez la performance mesurable, reproductible et difficile à casser par accident.

  • Gardez un petit ensemble de scènes de référence (GPU-heavy, CPU-heavy, worst-case).
  • Exécutez sur matériel/configuration fixe et stockez les résultats avec le hash de commit.
  • Surveillez p50/p95/p99, mémoire maximale et temps de chargement.
  • Définissez des seuils (ex. : p95 ne doit pas régresser de plus de 5 %).

Quand une régression apparaît : bisect, assignez un propriétaire, et revert rapidement si ça bloque la release.

Related posts