Aller au contenu
Ingénierie · 5 min de lecture

Le gain de performance le moins cher est le travail que vous ne faites pas

L'affirmation Quand une application est lente, l'instinct est de rendre le travail lent plus rapide — optimiser la requête, ajouter le cache, améliorer le matériel. Le gain plus gr...

A Rédigé par Administrator
Le gain de performance le moins cher est le travail que vous ne faites pas

L'affirmation

Quand une application est lente, l'instinct est de rendre le travail lent plus rapide — optimiser la requête, ajouter le cache, améliorer le matériel. Le gain plus grand et moins cher est généralement ailleurs : ne pas faire le travail du tout. Une fraction surprenante de ce que les applications calculent à chaque requête est un travail dont le résultat n'est jamais utilisé, est recalculé à l'identique des milliers de fois, ou répond à une question que personne n'a posée. Éliminer ce travail ne coûte rien à exécuter et ne casse jamais, car le code le plus rapide et le moins susceptible d'échouer est le code qui ne s'exécute pas.

Le travail calculé puis jeté

La première catégorie est le travail dont le résultat est écarté. Un point d'accès charge tout l'historique de commandes d'un client pour afficher son nom. Une page de liste calcule un total pour chaque ligne alors que seule la page courante est montrée. Un rapport calcule des chiffres pour toutes les régions quand l'utilisateur en a demandé une. Dans chaque cas, le travail s'exécute, consomme du temps et de la charge de base, et sa sortie est en grande partie écartée — et parce qu'il produit une réponse correcte, rien ne le signale comme gaspillage. Il est invisible précisément parce qu'il fonctionne ; il en fait simplement bien plus que la requête ne l'exigeait.

Le correctif est de calculer seulement ce que la réponse utilise réellement. Récupérez le nom, pas l'historique. Totalisez la page visible, pas toute la table. Interrogez la région demandée, pas toutes. Ce n'est pas de l'optimisation au sens habituel — vous ne rendez rien plus rapide — vous retirez un travail qui n'avait jamais besoin d'arriver, ce qui est à la fois moins cher et plus sûr que de l'accélérer.

Le travail répété à l'identique

La deuxième catégorie est le même calcul effectué encore et encore avec les mêmes intrants et le même résultat. Une valeur de configuration analysée à chaque requête au lieu d'une fois au démarrage. Un ensemble de permissions recalculé pour chaque élément d'une boucle alors qu'il est identique pour tous. Un taux de conversion récupéré par poste alors que chaque poste le partage. Aucun n'est coûteux individuellement, ce qui est pourquoi ils survivent — mais multiplié par chaque requête et chaque itération, le travail identique répété devient une grande fraction de la charge totale, sans rien faire de neuf à chaque fois.

// avant : la meme recherche, une fois par element de la boucle
for (const item of items) {
  const rate = fetchRate(item.currency);   // identique a chaque iteration
  ...
}
// apres : calculer les valeurs distinctes une fois, les reutiliser
const rates = fetchRatesFor(distinctCurrencies(items));
for (const item of items) { const rate = rates[item.currency]; ... }

Sortir le travail répété de la boucle, ou le calculer une fois et le réutiliser, le retire sans changer ce que le code produit. Le résultat est identique ; le travail est une fraction de ce qu'il était.

Le travail qui répond à une question non posée

La troisième catégorie est la plus insidieuse : des fonctions et des calculs bâtis pour des exigences qui se sont avérées inexistantes. Un point d'accès qui renvoie trente champs quand le client en utilise trois. Une tâche nocturne qui génère un rapport que personne ne lit. Un cache réchauffé pour des données rarement demandées. Ce travail n'est pas faux — il fait ce pour quoi il a été bâti — mais il ne sert aucun besoin réel, et il s'accumule parce que retirer des choses semble plus risqué que les laisser, de sorte que les systèmes développent un sédiment de calcul qui avait un sens jadis et ne sert rien maintenant.

Le trouver exige de demander, des choses coûteuses que fait votre système, qui consomme réellement la sortie. Le rapport sans lecteurs, les champs qu'aucun client ne demande, les données précalculées que personne n'interroge — chacun peut être retiré, et la façon de le retirer sûrement est de cesser de le produire et de guetter quiconque le remarque. Le silence signifie qu'il n'a jamais été nécessaire.

Comment trouver le travail à ne pas faire

La mesure qui révèle le travail inutile diffère du profilage ordinaire. Le profilage ordinaire vous dit ce qui est lent ; ici vous voulez aussi savoir ce qui est fréquent et si sa sortie est utilisée. Deux questions tranchent l'essentiel :

  • Pour les opérations les plus fréquentes, toute la sortie calculée est-elle réellement utilisée par la réponse ? Journalisez ce qu'un point d'accès calcule contre ce qu'il renvoie, et l'écart est un gaspillage candidat.
  • Pour les opérations les plus coûteuses, quelqu'un consomme-t-il le résultat ? Tracez la sortie jusqu'à un vrai lecteur ; si vous n'en trouvez pas, vous avez trouvé un travail à cesser.

C'est une lentille différente de « rendre la chose lente rapide », et elle trouve fréquemment de plus grands gains, car retirer une opération entièrement bat toute amélioration en pourcentage — une requête optimisée pour tourner deux fois plus vite tourne encore ; une requête retirée tourne zéro fois.

La discipline à venir

L'habitude qui empêche le travail inutile de s'accumuler est de questionner le besoin avant de bâtir la chose, et de retirer plutôt qu'optimiser quand un travail s'avère inutilisé. Avant d'ajouter un calcul, demandez ce qui consomme son résultat et si la requête devant vous l'exige réellement. Avant d'optimiser une opération lente, demandez si elle doit arriver du tout, ou aussi souvent, ou sur autant de données. L'amélioration de performance la plus fiable n'est pas une version plus rapide du travail — c'est la découverte que le travail n'était jamais nécessaire, retiré entièrement, ne coûtant rien à exécuter et n'apparaissant jamais dans un incident, car le code qui ne s'exécute pas ne peut pas échouer.

#performance #architecture #optimisation #engineering

À lire aussi