web-dev-qa-db-fra.com

Peut-on remplacer du code optimisé par du code lisible?

Parfois, vous rencontrez une situation dans laquelle vous devez étendre/améliorer du code existant. Vous voyez que l'ancien code est très léger, mais il est également difficile à étendre et prend du temps à lire.

Est-ce une bonne idée de le remplacer par du code moderne?

Il y a quelque temps, j'aimais l'approche Lean, mais maintenant, il me semble qu'il vaut mieux sacrifier beaucoup d'optimisations au profit d'abstractions plus élevées, de meilleures interfaces et d'un code plus lisible et extensible.

Les compilateurs semblent également s'améliorer, donc des choses comme struct abc = {} sont silencieusement transformés en memsets, shared_ptrs produisent à peu près le même code que le twiddling de pointeur brut, les modèles fonctionnent très bien car ils produisent du code super maigre, etc.

Mais encore, parfois, vous voyez des tableaux basés sur la pile et des anciennes fonctions C avec une logique obscure, et généralement ils ne sont pas sur le chemin critique.

Est-ce une bonne idée de changer un tel code si vous devez en toucher un petit morceau de toute façon?

79
Coder

Où?

  • Sur une page d'accueil d'un site Web à l'échelle de Google, ce n'est pas acceptable. Gardez les choses aussi vite que possible.

  • Dans une partie d'une application utilisée par une seule personne une fois par an, il est parfaitement acceptable de sacrifier les performances pour gagner en lisibilité du code.

En général, quelles sont les exigences non fonctionnelles pour la partie du code sur laquelle vous travaillez? Si une action doit effectuer moins de 900 ms. dans un contexte donné (machine, charge, etc.) 80% du temps, et en fait, il fonctionne sous 200 ms. 100% du temps, bien sûr, rendez le code plus lisible même s'il peut avoir un léger impact sur les performances. Si d'autre part la même action n'a jamais été exécutée en moins de dix secondes, eh bien, vous devriez plutôt essayer de voir ce qui ne va pas avec la performance (ou l'exigence en premier lieu).

Aussi, comment l'amélioration de la lisibilité diminuera les performances? Souvent, les développeurs adaptent le comportement à une optimisation prématurée: ils ont peur d'augmenter la lisibilité, pensant que cela détruirait considérablement les performances, tandis que les plus lisibles le code passera quelques microsecondes de plus à faire la même action.

115
Arseni Mourzenko

Habituellement, non.

La modification du code peut entraîner des problèmes de déclenchement imprévus ailleurs dans le système (qui peuvent parfois passer inaperçus jusqu'à bien plus tard dans un projet si vous n'avez pas de tests unitaires solides et de fumée en place). Je passe généralement par la mentalité "si ce n'est pas cassé, ne le répare pas".

L'exception à cette règle est si vous implémentez une nouvelle fonctionnalité qui touche ce code. Si, à ce stade, cela n'a pas de sens et que le refactoring doit vraiment avoir lieu, alors allez-y aussi longtemps que le temps de refactoring (et suffisamment de tests et de tampon pour traiter les problèmes de survenue) est tous pris en compte dans les estimations.

Bien sûr, profil, profil, profil , surtout s'il s'agit d'une zone de chemin critique.

36
Demian Brecht

En bref: cela dépend

  • Allez-vous vraiment avoir besoin ou utiliser votre version refactorisée/améliorée?

    • Y a-t-il un gain concret, immédiat ou à long terme?
    • Ce gain est-il uniquement pour la maintenabilité, ou vraiment architectural?
  • Doit-il vraiment être optimisé?

    • Pourquoi?
    • Quel gain cible devez-vous viser?

En détails

Allez-vous avoir besoin de choses nettoyées et brillantes?

Il y a des choses à faire attention ici, et vous devez identifier la limite entre ce qui est un gain réel et mesurable et ce qui est juste votre préférence personnelle et la mauvaise habitude potentielle de toucher du code qui ne devrait pas l'être.

Plus précisément, sachez ceci:

Il y a une telle chose comme Over-Engineering

C'est un anti-modèle, et il est livré avec des problèmes intégrés:

  • il peut être plus extensible , mais il peut ne pas être plus facile à étendre ,
  • il peut ne pas être plus simple à comprendre ,
  • dernier, mais certainement pas le moindre ici: vous pourriez ralentir tout le code.

Certains pourraient également mentionner le principe KISS comme référence, mais ici c'est contre-intuitif: la manière optimisée est-elle simple ou la manière architecturale propre? La réponse n'est pas nécessairement absolue, comme expliqué dans le reste ci-dessous.

Vous n'en aurez pas besoin

Le principe YAGNI n'est pas complètement orthogonal à l'autre problème, mais cela aide à se poser la question: allez-vous en avoir besoin?

L'architecture plus complexe présente-t-elle vraiment un avantage pour vous, en plus de donner l'apparence d'être plus maintenable?

Si ce n'est pas cassé, ne le réparez pas

Écrivez ceci sur une grande affiche et accrochez-le à côté de votre écran ou dans la cuisine au travail, ou dans la salle de réunion des développeurs. Bien sûr, il y a beaucoup d'autres mantras qui méritent d'être répétés, mais celui-ci est important chaque fois que vous essayez de faire des "travaux d'entretien" et que vous ressentez le besoin de "l'améliorer".

Il est naturel pour nous de vouloir "améliorer" le code ou même de le toucher, même inconsciemment, alors que nous le lisons pour essayer de le comprendre. C'est une bonne chose, car cela signifie que nous sommes opiniâtres et essayons d'avoir une compréhension plus profonde des internes, mais cela est également lié à notre niveau de compétence, à nos connaissances (comment décidez-vous de ce qui est mieux ou pas? Bien, voir les sections ci-dessous ...), et toutes les hypothèses que nous faisons sur ce que nous pensons connaître le logiciel ...:

  • en fait,
  • doit réellement faire,
  • devra éventuellement faire,
  • et comment il le fait.

Doit-il vraiment être optimisé?

Cela dit, pourquoi a-t-il été "optimisé" en premier lieu? Ils disent que optimisation prématurée est la racine de tout mal, et si vous voyez du code non documenté et apparemment optimisé, vous pouvez généralement supposer qu'il n'a probablement pas suivi les Rules of Optimization n'a pas vraiment besoin de l'effort d'optimisation et que ce n'était que l'hybris du développeur habituel. Encore une fois, c'est peut-être à vous de parler maintenant.

Si tel est le cas, dans quelles limites devient-il acceptable? S'il y a un besoin, cette limite existe et vous donne la possibilité d'améliorer les choses, ou une ligne dure pour décider de laisser tomber.

Méfiez-vous également des caractéristiques invisibles. Il y a de fortes chances que votre version "extensible" de ce code vous permette également d'augmenter la mémoire au moment de l'exécution et de présenter une empreinte mémoire statique encore plus grande pour l'exécutable. Shiny OO ont des coûts non intuitifs comme ceux-ci, et peuvent avoir une incidence sur votre programme et l'environnement sur lequel il est censé fonctionner.

Mesurer, mesurer, mesurer

En tant que Google maintenant, tout est question de données! Si vous pouvez le sauvegarder avec des données, alors c'est nécessaire.

Il y a ce conte pas si vieux que pour chaque dollar dépensé en développement, il sera suivi de au moins 1 $ en test et au moins 1 $ de support (mais vraiment, c'est beaucoup plus).

Le changement a un impact sur beaucoup de choses:

  • vous devrez peut-être produire une nouvelle version;
  • vous devriez écrire de nouveaux tests unitaires (certainement s'il n'y en avait pas, et votre architecture plus extensible laisse probablement de la place pour plus, car vous avez plus de surface pour les bogues);
  • vous devez écrire de nouveaux tests de performances (pour vous assurer que cela reste stable à l'avenir, et pour voir où se trouvent les goulots d'étranglement), et ce sont difficiles à faire ;
  • vous devrez le documenter (et plus extensible signifie plus de place pour les détails);
  • vous (ou quelqu'un d'autre) aurez besoin de le re-tester en profondeur dans le contrôle qualité;
  • le code n'est (presque) jamais exempt de bogues et vous devrez le prendre en charge.

C'est donc non seulement la consommation de ressources matérielles (vitesse d'exécution ou encombrement mémoire) que vous devez mesurer ici, c'est aussi ressources de l'équipe consommation. Les deux doivent être prédits pour définir un objectif cible, être mesurés, pris en compte et adaptés en fonction du développement.

Et pour vous manager, cela signifie l'adapter au plan de développement actuel, alors communiquez à ce sujet et n'entrez pas dans le codage furieux cow-boy/sous-marin/black-ops.


En général...

Oui mais...

Ne vous méprenez pas, en général, je serais en faveur de faire pourquoi vous suggérez, et je le préconise souvent. Mais vous devez être conscient du coût à long terme.

Dans un monde parfait, c'est la bonne solution:

  • matériel informatique s'améliore avec le temps,
  • les compilateurs et les plates-formes d'exécution s'améliorent avec le temps,
  • vous obtenez un code proche de la perfection, propre, maintenable et lisible.

En pratique:

  • vous pourriez aggraver

    Vous avez besoin de plus de globes oculaires pour le regarder, et plus vous le complexifiez, plus vous avez besoin de globes oculaires.

  • tu ne peux pas prédire l'avenir

    Vous ne pouvez pas savoir avec une certitude absolue si vous en aurez jamais besoin et pas même si les "extensions" dont vous aurez besoin auraient été plus faciles et plus rapides à implémenter dans l'ancienne forme, et si elles-mêmes auraient besoin d'être super-optimisées .

  • cela représente, du point de vue de la direction, un coût énorme sans gain direct.

Faites-en partie du processus

Vous mentionnez ici qu'il s'agit d'un assez petit changement et vous avez des problèmes spécifiques à l'esprit. Je dirais que c'est généralement OK dans ce cas, mais la plupart d'entre nous ont également des histoires personnelles de petits changements, des modifications presque chirurgicales, qui se sont finalement transformées en cauchemar de maintenance et des délais presque manqués ou explosés parce que Joe Programmer n'en a pas vu un des raisons derrière le code et a touché quelque chose qui n'aurait pas dû être.

Si vous avez un processus pour gérer de telles décisions, vous en retirez l'avantage personnel:

  • Si vous testez les choses correctement, vous saurez plus rapidement si les choses sont cassées,
  • Si vous les mesurez, vous saurez s'ils se sont améliorés,
  • Si vous l'examinez, vous saurez si cela décourage les gens.

La couverture des tests, le profilage et la collecte de données sont difficiles

Mais, bien sûr, votre code de test et vos métriques peuvent souffrir des mêmes problèmes que vous essayez d'éviter pour votre code actuel: testez-vous les bonnes choses, et sont-elles la bonne chose pour l'avenir, et mesurez-vous la bonne des choses?

Pourtant, en général, plus vous testez (jusqu'à une certaine limite) et mesurez, plus vous collectez de données et plus vous êtes en sécurité. Mauvais temps d'analogie: pensez-y comme au volant (ou à la vie en général): vous pouvez être le meilleur conducteur du monde, si la voiture tombe en panne sur vous ou que quelqu'un décide de se tuer en conduisant dans votre voiture avec la sienne aujourd'hui, votre les compétences pourraient ne pas suffire. Il y a deux choses environnementales qui peuvent vous frapper, et les erreurs humaines comptent également.

Les revues de code sont les tests de couloir de l'équipe de développement

Et je pense que la dernière partie est clé ici: faites des revues de code. Vous ne connaîtrez pas la valeur de vos améliorations si vous les faites en solo. Les révisions de code sont nos "tests de couloir": suivez la version de Raymond de la loi de Linus à la fois pour détecter les bogues et détecter la suringénierie et autres anti-modèles, et pour vous assurer que le code est en ligne avec votre les capacités de l'équipe. Il n'y a aucun intérêt à avoir le "meilleur" code si personne d'autre que vous ne pouvez le comprendre et le maintenir, et cela vaut à la fois pour les optimisations cryptiques et les conceptions architecturales profondes à 6 couches.

En guise de conclusion, souvenez-vous:

Tout le monde sait que le débogage est deux fois plus difficile que d'écrire un programme en premier lieu. Donc, si vous êtes aussi intelligent que possible lorsque vous l'écrivez, comment allez-vous le déboguer? - Brian Kernighan

29
haylem

En général, vous devez d'abord vous concentrer sur la lisibilité et les performances beaucoup plus tard. La plupart du temps, ces optimisations de performances sont de toute façon négligeables, mais le coût de maintenance peut être énorme.

Certes, toutes les "petites" choses devraient être changées en faveur de la clarté car, comme vous l'avez souligné, la plupart d'entre elles seront de toute façon optimisées par le compilateur.

En ce qui concerne les optimisations plus importantes, il peut y avoir une chance que les optimisations soient réellement essentielles pour atteindre des performances raisonnables (bien que ce ne soit pas souvent le cas de manière surprenante). Je ferais vos changements, puis je profilerais le code avant et après les changements. Si le nouveau code présente des problèmes de performances importants, vous pouvez toujours revenir à la version optimisée, sinon vous pouvez simplement vous en tenir à la version de code plus propre.

Modifiez une seule partie du code à la fois et voyez comment cela affecte les performances après chaque cycle de refactoring.

8
Oleksi

Cela dépend de la raison pour laquelle le code a été optimisé et de l'effet de sa modification et de son impact sur les performances globales. Cela devrait également dépendre de la bonne manière de charger les modifications de test.

Vous ne devez pas effectuer cette modification sans profilage avant et après et de préférence sous une charge similaire à celle qui serait observée en production. Cela signifie ne pas utiliser un minuscule sous-ensemble de données sur une machine de développeur ou tester lorsqu'un seul utilisateur utilise le système.

Si l'optimisation était récente, vous pourrez peut-être parler au développeur et savoir exactement quel était le problème et à quel point l'application était lente avant l'optimisation. Cela peut vous en dire beaucoup sur l'opportunité de faire l'optimisation et les conditions pour lesquelles l'optimisation était nécessaire (un rapport qui couvre une année entière par exemple peut ne pas être devenu lent avant septembre ou octobre, si vous testez votre changement en février, la lenteur n'est peut-être pas encore apparente et le test invalide).

Si l'optimisation est plutôt ancienne, les nouvelles méthodes pourraient même être plus rapides et plus lisibles.

En fin de compte, c'est une question pour votre patron. Il faut beaucoup de temps pour refactoriser quelque chose qui a été optimisé et pour s'assurer que le changement n'a pas affecté le résultat final et qu'il fonctionne aussi bien ou du moins de manière acceptable par rapport à l'ancienne méthode. Il voudra peut-être que vous passiez votre temps dans d'autres domaines au lieu d'assumer une tâche à haut risque pour gagner quelques minutes de temps de codage. Ou il peut convenir que le code est difficile à comprendre et a nécessité une intervention fréquente et que de meilleures méthodes sont maintenant disponibles.

8
HLGEM

si profilage montre que l'optimisation n'est pas nécessaire (elle ne se trouve pas dans une section critique) ou a même un temps d'exécution pire (à la suite d'une mauvaise optimisation prématurée), remplacez bien par le code lisible qui est plus facile à maintenir

assurez-vous également que le code se comporte de la même manière avec les tests appropriés

6
ratchet freak

Pensez-y d'un point de vue commercial. Quels sont les coûts du changement? De combien de temps avez-vous besoin pour effectuer la modification et combien économiserez-vous à long terme en rendant le code plus facile à étendre ou à maintenir? Maintenant, attachez un prix à ce moment et comparez-le à l'argent perdu en réduisant les performances. Vous devrez peut-être ajouter ou mettre à niveau un serveur pour compenser la perte de performances. Peut-être que le produit ne répond plus aux exigences et ne peut plus être vendu. Il n'y a peut-être aucune perte. Peut-être que le changement augmente la robustesse et fait gagner du temps ailleurs. Maintenant, prenez votre décision.

Par ailleurs, dans certains cas, il peut être possible de conserver les deux versions d'un fragment. Vous pouvez écrire un test générant des valeurs d'entrée aléatoires et vérifier les résultats avec l'autre version. Utilisez la solution "intelligente" pour vérifier le résultat d'une solution parfaitement compréhensible et évidemment correcte et ainsi obtenir une certaine assurance (mais aucune preuve) que la nouvelle solution est équivalente à l'ancienne. Ou allez dans l'autre sens et vérifiez le résultat du code délicat avec le code détaillé et documentez ainsi l'intention derrière le hack de manière non ambiguë.

5
scarfridge

Fondamentalement, vous demandez si refactoring est une entreprise valable. La réponse à cette question est certainement oui.

Mais...

... vous devez le faire avec soin. Vous avez besoin de tests unitaires, d'intégration, fonctionnels et de performances solides pour tout code que vous refactorisez. Vous devez être sûr qu'ils testent vraiment toutes les fonctionnalités requises. Vous devez pouvoir les exécuter facilement et à plusieurs reprises. Une fois que vous avez cela, vous devriez pouvoir remplacer les composants par de nouveaux composants contenant la fonctionnalité équivalente.

Martin Fowler a écrit le livre à ce sujet.

4
Matthew Flynn

La réponse est, sans perte de généralité, oui. Ajoutez toujours du code moderne lorsque vous voyez du code difficile à lire et supprimez le mauvais code dans la plupart des cas. J'utilise le processus suivant:

  1. Recherchez le test de performances et les informations de profilage associées. S'il n'y a pas de test de performance, ce qui peut être affirmé sans preuve peut être rejeté sans preuve. Affirmez que votre code moderne est plus rapide et supprimez l'ancien code. Si quelqu'un soutient (même vous-même), demandez-lui d'écrire le code de profilage pour prouver lequel est le plus rapide.
  2. Si le code de profilage existe, écrivez quand même le code moderne. Nommez-le quelque chose comme <function>_clean(). Ensuite, "faites la course" à votre code contre le mauvais code. Si votre code est meilleur, supprimez l'ancien code.
  3. Si l'ancien code est plus rapide, laissez quand même votre code moderne dedans. Il sert de bonne documentation pour ce que l'autre code est censé faire, et puisque le code "race" est là, vous pouvez continuer à l'exécuter pour documenter les caractéristiques de performances et les différences entre les deux chemins. Vous pouvez également tester de manière unitaire les différences de comportement du code. Surtout, le code moderne battra un jour le code "optimisé", garanti. Vous pouvez ensuite supprimer le mauvais code.

QED.

3
Sunny Kalsi

Vous ne devez pas modifier le code de production et de travail sans raison valable. Le "refactoring" n'est pas une raison suffisante, sauf si vous ne pouvez pas faire votre travail sans ce refactoring. Même si ce que vous faites est de corriger des bogues dans le code difficile lui-même, vous devez prendre le temps de le comprendre et apporter le plus petit changement possible. Si le code est difficile à comprendre, vous ne pourrez pas le comprendre complètement, et donc tout changement que vous apporterez aura des effets secondaires imprévisibles - des bogues, en d'autres termes. Plus le changement est important, plus vous risquez de causer des problèmes.

Il y aurait une exception à cela: si le code incompréhensible avait un ensemble complet de tests unitaires, vous pourriez le refactoriser. Comme je n'ai jamais vu ou entendu parler de code incompréhensible avec des tests unitaires complets, vous écrivez d'abord les tests unitaires, obtenez l'accord des personnes nécessaires que ces tests unitaires représentent en fait ce que le code devrait faire, puis apportez les modifications de code . Je l'ai fait une ou deux fois; c'est une douleur dans le cou, et très cher, mais donne finalement de bons résultats.

3
mjfgates

Si c'est juste un court morceau de code qui fait quelque chose de relativement simple d'une manière difficile à comprendre, je changerais la "compréhension rapide" dans un commentaire étendu et/ou une implémentation alternative inutilisée, comme

#ifdef READABLE_ALT_IMPLEMENTATION

   double x=0;
   for(double n: summands)
     x += n;
   return x;

#else

   auto subsum = [&](int lb, int rb){
          double x=0;
          while(lb<rb)
            x += summands[lb++];
          return x;
        };
   double x_fin=0;
   for(double nsm: par_eval( subsum
                           , partitions(n_threads, 0, summands.size()) ) )
     x_fin += nsm;
   return x_fin;

#endif
3
leftaroundabout

Si je pouvais enseigner une chose au monde (à propos des logiciels) avant de mourir, je lui enseignerais que "Performance contre X" est un faux dilemme.

Le refactoring est généralement connu comme une aubaine pour la lisibilité et la fiabilité, mais il peut tout aussi facilement prendre en charge l'optimisation. Lorsque vous gérez l'amélioration des performances comme une série de refactorisations, vous pouvez respecter la règle du camping tout en accélérant l'application. Il vous appartient en fait, du moins à mon avis, de le faire sur le plan éthique.

Par exemple, l'auteur de cette question a rencontré un morceau de code fou. Si cette personne lisait mon code, elle trouverait que la partie folle est de 3 à 4 lignes. Il se trouve dans une méthode en elle-même, et le nom et la description de la méthode indiquent ce que fait la méthode. La méthode contiendrait de 2 à 6 lignes de commentaires en ligne décrivant COMMENT le code fou obtient la bonne réponse, malgré son apparence douteuse.

Compartimenté de cette manière, vous êtes libre d'échanger les implémentations de cette méthode à votre guise. En effet, c'est probablement comme ça que j'ai écrit la version folle pour commencer. Vous êtes invités à essayer, ou au moins à demander des alternatives. La plupart du temps, vous découvrirez que l'implémentation naïve est nettement pire (généralement je ne me soucie que d'une amélioration de 2 à 10 fois), mais les compilateurs et les bibliothèques changent toujours, et qui sait ce que vous pouvez trouver aujourd'hui qui n'était pas disponible lorsque la fonction a été écrite?

3
Jason

Ce n'est probablement pas une bonne idée de le toucher - si le code a été écrit de cette façon pour des raisons de performances, cela signifie que le modifier pourrait ramener des problèmes de performances qui avaient été précédemment résolus.

Si vous faites décidez de changer les choses pour qu'elles soient plus lisibles et extensibles: Avant de faire un changement, comparez l'ancien code sous lourd charge. Encore mieux si vous pouvez trouver un ancien document ou un ticket d'incident décrivant le problème de performances que ce code étrange est censé résoudre. Ensuite, après avoir apporté vos modifications, réexécutez les tests de performances. Si ce n'est pas très différent, ou toujours dans des paramètres acceptables, alors c'est probablement OK.

Il peut parfois arriver que lorsque d'autres parties d'un système changent, ce code optimisé pour les performances n'a plus besoin d'optimisations aussi lourdes, mais il n'y a aucun moyen de le savoir avec certitude sans tests rigoureux.

Le problème ici est de distinguer "optimisé" de lisible et extensible, ce que nous, les utilisateurs, considérons comme du code optimisé et ce que le compilateur voit comme optimisé sont deux choses différentes. Le code que vous cherchez à changer n'est peut-être pas du tout un goulot d'étranglement, et donc même si le code est "maigre", il n'est même pas nécessaire qu'il soit "optimisé". Ou si le code est assez ancien, il peut y avoir des optimisations apportées par le compilateur aux éléments intégrés qui rendent l'utilisation d'une structure intégrée simple plus récente aussi efficace ou plus efficace que l'ancien code.

Et le "lean", le code illisible n'est pas toujours optimisé.

J'avais l'habitude de penser que le code intelligent/maigre était un bon code, mais profitant parfois de règles obscures du langage blessait plutôt que d'aider à la création de code, j'ai été mordu plus souvent qu'autrement dans tout travail intégré lorsque j'essayais de soyez intelligent parce que le compilateur transforme votre code intelligent en quelque chose de totalement inutilisable par le matériel embarqué.

2
Jeff Langemeier

Je ne remplacerai jamais le code optimisé par du code lisible car je ne peux pas compromettre les performances et je choisirai d'utiliser des commentaires appropriés à chaque section afin que tout le monde puisse comprendre la logique implémentée dans cette section optimisée qui résoudra les deux problèmes.

Par conséquent, le code sera optimisé + des commentaires appropriés le rendront également lisible.

REMARQUE: vous pouvez rendre un code optimisé lisible à l'aide de commentaires appropriés, mais vous ne pouvez pas rendre un code lisible optimisé.

2
Logicalj

Voici un exemple pour voir la différence entre un code simple et un code optimisé: https://stackoverflow.com/a/11227902/1396264

vers la fin de la réponse, il remplace simplement:

if (data[c] >= 128)
    sum += data[c];

avec:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

Pour être honnête, je n'ai aucune idée de ce avec quoi la déclaration if a été remplacée, mais comme le répondeur le dit, certaines opérations au niveau du bit donnent le même résultat (je vais juste prendre sa parole pour cela) .

Cela s'exécute en moins d'un quart du temps d'origine (11,54 s vs 2,5 s)

2
vedant

La question principale ici est: l'optimisation est-elle requise?

Si c'est le cas, vous ne pouvez pas le remplacer par du code plus lent et plus lisible. Vous devrez y ajouter des commentaires, etc. pour le rendre plus lisible.

Si le code n'a pas besoin d'être optimisé, il ne devrait pas l'être (au point d'affecter la lisibilité) et vous pouvez le re-factoriser pour le rendre plus lisible.

TOUTEFOIS - assurez-vous de savoir exactement ce que fait le code et comment le tester complètement avant de commencer à changer les choses. Cela inclut l'utilisation de pointe, etc. Si vous n'avez pas à composer un ensemble de cas de test et à les exécuter avant et après, vous n'avez pas le temps de refactoriser.

1
Stefan

Voici comment je fais les choses: d'abord je le fais fonctionner en code lisible, puis je l'optimise. Je garde la source d'origine et documente mes étapes d'optimisation.

Ensuite, lorsque j'ai besoin d'ajouter une fonctionnalité, je reviens à mon code lisible, j'ajoute la fonctionnalité et je suis les étapes d'optimisation que j'ai documentées. Parce que vous avez documenté, il est très rapide et facile de réoptimiser votre code avec la nouvelle fonctionnalité.

1
Pieter B

L'optimisation est relative. Par exemple:

Considérez une classe avec un groupe de membres BOOL:

// no nitpicking over BOOL vs bool allowed
class Pear {
 ...
 BOOL m_peeled;
 BOOL m_sliced;
 BOOL m_pitted;
 BOOL m_rotten;
 ...
};

Vous pourriez être tenté de convertir les champs BOOL en champs binaires:

class Pear {
 ...
 BOOL m_peeled:1;
 BOOL m_sliced:1;
 BOOL m_pitted:1;
 BOOL m_rotten:1;
 ...
};

Puisqu'un BOOL est typé comme INT (qui sur les plates-formes Windows est un entier 32 bits signé), cela prend seize octets et les regroupe en un seul. C'est une économie de 93%! Qui pourrait s'en plaindre?

Cette hypothèse:

Puisqu'un BOOL est typé comme INT (qui sur les plates-formes Windows est un entier 32 bits signé), cela prend seize octets et les regroupe en un seul. C'est une économie de 93%! Qui pourrait s'en plaindre?

mène à:

La conversion d'un BOOL en champ mono-bit a permis d'économiser trois octets de données mais vous a coûté huit octets de code lorsque le membre se voit attribuer une valeur non constante. De même, l'extraction de la valeur devient plus coûteuse.

Ce qui était

 Push [ebx+01Ch]      ; m_sliced
 call _Something@4    ; Something(m_sliced);

devient

 mov  ecx, [ebx+01Ch] ; load bitfield value
 shl  ecx, 30         ; put bit at top
 sar  ecx, 31         ; move down and sign extend
 Push ecx
 call _Something@4    ; Something(m_sliced);

La version bitfield est plus grande de neuf octets.

Asseyons-nous et faisons de l'arithmétique. Supposons que chacun de ces champs binaires soit accessible six fois dans votre code, trois fois pour l'écriture et trois fois pour la lecture. Le coût de la croissance du code est d'environ 100 octets. Il ne sera pas exactement de 102 octets car l'optimiseur peut être en mesure de tirer parti des valeurs déjà présentes dans les registres pour certaines opérations, et les instructions supplémentaires peuvent avoir des coûts cachés en termes de flexibilité réduite des registres. La différence réelle peut être plus, elle peut être moins, mais pour un calcul en arrière-plan, appelons-le 100. Pendant ce temps, l'économie de mémoire était de 15 octets par classe. Par conséquent, le seuil de rentabilité est de sept. Si votre programme crée moins de sept instances de cette classe, le coût du code dépasse les économies de données: votre optimisation de la mémoire était une désoptimisation de la mémoire.

Références

0
Paul Sweatte

La lisibilité à mon humble avis est plus importante que le code optimisé car dans la plupart des cas, la micro-optimisation n'en vaut pas la peine.

Article sur micro-optimisations non-sens :

Comme la plupart d'entre nous, je suis fatigué de lire des articles de blog sur les micro-optimisations non sensées comme le remplacement de l'impression par l'écho, ++ $ i par $ i ++, ou les guillemets doubles par des guillemets simples. Pourquoi? Parce que 99,999999% du temps, ce n'est pas pertinent.

"print" utilise un opcode de plus que "echo" car il retourne réellement quelque chose. Nous pouvons conclure que l'écho est plus rapide que l'impression. Mais un opcode ne coûte rien, vraiment rien.

J'ai essayé une nouvelle installation WordPress. Le script s'arrête avant qu'il ne se termine par une "erreur de bus" sur mon ordinateur portable, mais le nombre d'opcodes était déjà supérieur à 2,3 millions. Assez dit.

0
webvitaly