web-dev-qa-db-fra.com

Code clair et lisible vs code rapide et difficile à lire. Quand franchir la ligne?

Lorsque j'écris du code, j'essaie toujours de rendre mon code aussi propre et lisible que possible.

De temps en temps, il arrive un moment où vous devez franchir la ligne et passer du code propre de Nice au code légèrement plus laid pour le rendre plus rapide.

Quand est-ce OK de franchir cette ligne?

67
Ken Cochrane

Vous franchissez la ligne lorsque

  • Vous avez mesuré que votre code est trop lent pour son utilisation prévue.
  • Vous avez essayé d'autres améliorations qui ne nécessitent pas de nettoyer le code.

Voici un exemple concret: un système expérimental que je dirige produisait des données trop lentement, prenant plus de 9 heures par exécution et n'utilisant que 40% du processeur. Plutôt que de trop gâcher le code, j'ai déplacé tous les fichiers temporaires vers un système de fichiers en mémoire. Ajout de 8 nouvelles lignes de code non laid, et maintenant l'utilisation du CPU est supérieure à 98%. Problème résolu; aucune laideur requise.

118
Norman Ramsey

C'est une fausse dichotomie. Vous pouvez rendre le code rapide et facile à maintenir.

La façon dont vous le faites est de l'écrire propre, en particulier avec une structure de données aussi simple que possible.

Ensuite, vous découvrez où sont les vidanges de temps (en l'exécutant, après vous l'avez écrit, pas avant), et les corrigez un par un. (Voici un exemple.)

Ajouté: Nous entendons toujours parler de compromis, n'est-ce pas, comme un compromis entre le temps et la mémoire, ou un compromis entre la vitesse et la maintenabilité? Bien que de telles courbes puissent bien exister, il ne faut pas supposer qu'un programme donné est sur la courbe, ni même n'importe où près de lui.

Tout programme qui est sur la courbe peut facilement (en le donnant à un certain type de programmeur) être rendu à la fois beaucoup plus lent et beaucoup moins maintenable, et il sera alors loin de la courbe. Un tel programme a alors beaucoup de place pour être rendu plus rapide et plus facile à maintenir.

D'après mon expérience, c'est là que de nombreux programmes commencent.

58
Mike Dunlavey

Dans mon existence OSS, je fais beaucoup de travail de bibliothèque visant à la performance, qui est profondément lié à la structure de données appelant (c'est-à-dire externe à la bibliothèque), sans (par conception) aucun mandat sur les types entrants. Ici, la meilleure façon de rendre cela performant est la méta-programmation, qui (puisque je suis en .NET-land) signifie IL-emit. C'est du code moche et moche, mais très rapide.

De cette façon, j'accepte avec plaisir bibliothèque le code peut être "plus laid" que application code, simplement parce que il a moins ( ou peut-être pas) de contrôle sur les entrées, doit donc réaliser certaines tâches à travers différents mécanismes. Ou comme je l'ai exprimé l'autre jour:

"coder sur la falaise de la folie, pour que vous n'ayez pas à le faire"

Maintenant application le code est légèrement différent, car c'est là que les développeurs "réguliers" (sensés) investissent généralement une grande partie de leur temps collaboratif/professionnel; les objectifs et les attentes de chacun sont (OMI) légèrement différents.

OMI, les réponses ci-dessus qui suggèrent qu'il peut être rapide et facile à entretenir font référence à application code où le développeur a plus de contrôle sur les structures de données, et n'utilise pas d'outils comme la méta-programmation. Cela dit, il existe différentes façons de faire de la méta-programmation, avec différents niveaux de folie et différents niveaux de frais généraux. Même dans ce domaine, vous devez choisir le niveau d'abstraction approprié. Mais quand vous voulez activement, positivement, vraiment qu'il gère données inattendues de la manière la plus rapide; ça peut devenir moche. Traitez-le; p

31
Marc Gravell

Lorsque vous avez profilé le code et vérifié qu'il provoque réellement un ralentissement important.

27
Joel Rein

Le code propre n'est pas nécessairement exclusif avec le code à exécution rapide. Le code normalement difficile à lire a été écrit parce qu'il était plus rapide à écrire, pas parce qu'il s'exécute plus rapidement.

Il est sans doute imprudent d'écrire du code "sale" pour tenter de le rendre plus rapide, car vous ne savez pas avec certitude que vos modifications améliorent réellement quoi que ce soit. Knuth le dit le mieux:

"Nous devons oublier les petites efficacités, disons environ 97% du temps: l'optimisation prématurée est la racine de tout mal. Pourtant, nous ne devons pas laisser passer nos opportunités dans ces 3% critiques. Un bon programmeur ne pas être bercé de complaisance par un tel raisonnement, il sera sage de regarder attentivement le code critique, mais seulement après que ce code a été identifié. "

En d'autres termes, écrivez d'abord le code propre. Ensuite, profilez le programme résultant et voyez si ce segment est en fait un goulot d'étranglement des performances. Si tel est le cas, optimisez la section si nécessaire et assurez-vous d'inclure de nombreux commentaires sur la documentation (y compris éventuellement le code d'origine) pour expliquer les optimisations. Ensuite, profilez le résultat pour vérifier que vous avez réellement apporté une amélioration.

13
tylerl

Comme la question dit "code rapide difficile à lire ", la réponse simple n'est jamais. Il n'y a jamais d'excuse pour écrire du code difficile à lire. Pourquoi? Deux raisons.

  1. Que se passe-t-il si vous êtes renversé par un bus sur le chemin du retour ce soir? Ou (plus optimiste et plus généralement) retiré ce projet et réaffecté à autre chose? Le petit avantage que vous imaginez avoir fait avec votre désordre de code enchevêtré est totalement compensé par le fait que personne d'autre ne peut le comprendre . Le risque que cela pose aux projets logiciels est difficile à surestimer. J'ai travaillé une fois avec un fabricant majeur PBX (si vous travaillez dans un bureau, vous avez probablement l'un de leurs téléphones sur votre bureau). Leur chef de projet m'a dit un jour que leur produit de base - le logiciel propriétaire qui transformait une box Linux standard en un échange téléphonique complet - était connu dans l'entreprise sous le nom de "blob". Personne ne le comprenait plus. Chaque fois qu'ils ont implémenté une nouvelle fonctionnalité. ils avaient frappé compiler puis reculer, fermer les yeux, compter jusqu'à vingt, puis jeter un œil entre leurs doigts pour voir si cela fonctionnait. Aucune entreprise n'a besoin d'un produit de base qu'elle ne contrôle plus, mais c'est un scénario extrêmement répandu.
  2. Mais je dois optimiser! OK, vous avez donc suivi tous les excellents conseils dans les autres réponses à cette question: votre code échoue à ses tests de performances , vous l'avez soigneusement profilé, identifié les goulots d'étranglement, trouvé une solution ... et cela va impliquer un peu bit-twiddling . Très bien: maintenant allez-y et optimisez. Mais voici le secret (et vous voudrez peut-être vous asseoir pour celui-ci): l'optimisation et la réduction de la taille du code source ne sont pas la même chose . Les commentaires, les espaces blancs, les crochets et les noms de variables significatifs sont tous des aides énormes à la lisibilité qui ne vous coûtent absolument rien car le compilateur les jettera. (Ou si vous écrivez un langage non compilé comme JavaScript - et oui, il y a des raisons très valables d'optimiser JavaScript - ils peuvent être traités par un compresseur .) De longues lignes de crampes, minimalistes le code (comme celui que muntoo a posté ici ) n'a rien à voir avec l'optimisation: c'est un programmeur essayant de montrer à quel point ils sont intelligents en emballant autant de code en aussi peu de caractères que possible. Ce n'est pas intelligent, c'est stupide. Un programmeur vraiment intelligent est celui qui peut communiquer clairement ses idées aux autres.
10
Mark Whitaker

Quand c'est du code jetable. Je veux dire littéralement: lorsque vous écrivez un script pour effectuer un calcul ou une tâche ponctuelle, et que vous savez avec une telle certitude que vous n'aurez plus jamais à refaire cette action que vous pouvez `` rm source-file '' sans hésitation, alors vous pouvez choisir la route laide.

Sinon, c'est une fausse dichotomie - si vous pensez que vous avez besoin de faire du mal pour le faire plus rapidement, vous le faites mal. (Ou vos principes sur ce qu'est un bon code doivent être révisés. L'utilisation de goto est en fait assez élégante quand c'est la bonne solution au problème. Elle l'est rarement cependant.)

4
maaku

Chaque fois que le coût estimé d'une baisse des performances sur le marché est supérieur au coût estimé de la maintenance du code pour le module de code en question.

Les gens font encore des codes SSE/NEON/etc torsadés codés à la main. Assemblage pour essayer de battre le logiciel de certains concurrents sur la puce CPU populaire de cette année.

3
hotpaw2

N'oubliez pas que vous pouvez rendre le code difficile à lire facile à comprendre par une documentation et des commentaires appropriés.

En général, profilez après avoir écrit du code facile à lire qui fait la fonction souhaitée. Les goulots d'étranglement peuvent vous obliger à faire quelque chose qui rend les choses plus compliquées, mais vous pouvez résoudre ce problème en vous expliquant.

3
Carlos

Pour moi, c'est une proportion de stabilité (comme dans le ciment cimenté, l'argile cuite au four, sertie dans la pierre, écrite à l'encre permanente). Plus votre code est instable, car plus il est probable que vous devrez le changer à l'avenir, plus il doit être facilement pliable, comme l'argile humide, pour rester productif. J'insiste également sur la flexibilité et non sur la lisibilité. Pour moi, la facilité de changement de code est plus importante que la facilité de lecture. Le code peut être facile à lire et un cauchemar à changer, et à quoi sert de lire et de comprendre facilement les détails de l'implémentation s'il s'agit d'un cauchemar à changer? À moins qu'il ne s'agisse que d'un exercice académique, le but de pouvoir facilement comprendre le code dans une base de code de production est généralement de pouvoir le modifier plus facilement selon les besoins. S'il est difficile de changer, alors la plupart des avantages de la lisibilité disparaissent. La lisibilité n'est généralement utile que dans le contexte de la flexibilité, et la flexibilité n'est utile que dans le contexte de l'instabilité.

Naturellement, même le code le plus difficile à maintenir imaginable, quelle que soit sa facilité ou sa difficulté de lecture, ne pose pas de problème s'il n'y a jamais de raison de le modifier, il suffit de l'utiliser. Et il est possible d'obtenir une telle qualité, en particulier pour le code système de bas niveau où les performances ont souvent tendance à compter le plus. J'ai un code C que j'utilise toujours régulièrement et qui n'a pas changé depuis la fin des années 80. Il n'a pas eu besoin de changer depuis. Le code est énormément, écrit dans les jours de tripotage, et je le comprends à peine. Pourtant, il est toujours applicable aujourd'hui, et je n'ai pas besoin de comprendre sa mise en œuvre pour en tirer le meilleur parti.

Une rédaction approfondie des tests est un moyen d'améliorer la stabilité. Un autre est le découplage. Si votre code ne dépend de rien d'autre, alors la seule raison pour laquelle il change est s'il doit lui-même changer. Parfois, une petite quantité de duplication de code peut servir de mécanisme de découplage pour améliorer considérablement la stabilité d'une manière qui en fait un compromis valable si, en échange, vous obtenez du code qui est maintenant complètement indépendant de toute autre chose. Maintenant, ce code est invulnérable aux modifications du monde extérieur. Pendant ce temps, le code qui dépend de 10 bibliothèques externes différentes a 10 fois plus de raisons de changer à l'avenir.

Une autre chose utile dans la pratique est de séparer votre bibliothèque des parties instables de votre base de code, peut-être même de la construire séparément, comme vous pouvez le faire pour les bibliothèques tierces (qui sont également destinées à être utilisées, non modifiées, du moins pas par votre équipe). Ce type d'organisation peut empêcher les gens de la falsifier.

Un autre est le minimalisme. Moins votre code essaie de faire, plus il est susceptible de faire ce qu'il fait bien. Les conceptions monolithiques sont presque instables en permanence, car de plus en plus de fonctionnalités leur sont ajoutées, plus elles semblent incomplètes.

La stabilité devrait être votre objectif principal chaque fois que vous essayez d'écrire du code qui sera inévitablement difficile à modifier, comme le code SIMD parallélisé qui a été micro-réglé à mort. Vous contrecarrez la difficulté de maintenir le code en maximisant la probabilité que vous n'aurez pas à changer le code, et donc ne devrez pas le maintenir à l'avenir. Cela réduit les coûts de maintenance à zéro, quelle que soit la difficulté de maintenance du code.

0
user204677