Un de mes collègues a engagé aujourd'hui une classe appelée ThreadLocalFormat
, qui a essentiellement déplacé les instances de Java Formater les classes dans un thread local, car elles ne sont pas thread-safe et "relativement coûteuses" pour J'ai écrit un test rapide et calculé que je pouvais créer 200 000 instances par seconde, lui ai demandé s'il en créait autant, ce à quoi il a répondu "loin d'être si nombreux". C'est un excellent programmeur et tout le monde dans l'équipe est nous n'avons aucun problème à comprendre le code résultant, mais il s'agissait clairement d'optimiser là où il n'y en a pas vraiment besoin. Il a sauvegardé le code à ma demande. Que pensez-vous? S'agit-il d'un cas "d'optimisation prématurée" et à quel point est ce que c'est vraiment?
Il est important de garder à l'esprit la citation complète:
Il faut 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.
Cela signifie que, en l'absence de problèmes de performances mesurés, vous ne devez pas optimiser car vous pensez vous obtiendrez un gain de performances. Il existe des optimisations évidentes (comme ne pas faire de concaténation de chaînes dans une boucle étroite), mais tout ce qui n'est pas une optimisation triviale doit être évité jusqu'à ce qu'il puisse être mesuré.
Le plus gros problème avec "l'optimisation prématurée" est qu'elle peut introduire des bogues inattendus et peut être une perte de temps énorme.
Les optimisations prématurées micro sont la racine de tout mal, car les micro-optimisations laissent de côté le contexte. Ils ne se comportent presque jamais comme on les attend.
Quelles sont les bonnes optimisations initiales par ordre d'importance:
Quelques optimisations au milieu du cycle de développement:
Quelques optimisations de fin de cycle de développement
Toutes les premières optimisations ne sont pas mauvaises, les micro-optimisations sont mauvaises si elles sont effectuées au mauvais moment dans le cycle de vie du développement, car elles peuvent affecter négativement l'architecture, peuvent affecter négativement la productivité initiale, peuvent être sans rapport avec les performances ou même avoir un effet néfaste en fin de développement en raison de conditions environnementales différentes.
Si les performances sont préoccupantes (et devraient toujours l'être), pensez toujours gros. La performance est une image plus grande et ne concerne pas des choses comme: dois-je utiliser int ou long ?. Optez pour de haut en bas lorsque vous travaillez avec des performances au lieu de de bas en haut .
optimisation sans première mesure est presque toujours prématuré.
Je crois que c'est vrai dans ce cas, et vrai dans le cas général également.
L'optimisation est "mauvaise" si elle provoque:
Dans votre cas, il semble qu'un peu de temps de programmeur ait déjà été dépensé, le code n'était pas trop complexe (une supposition de votre commentaire que tout le monde dans l'équipe serait en mesure de comprendre), et le code est un peu plus à l'épreuve du temps (étant thread sûr maintenant, si j'ai bien compris votre description). Cela ne ressemble qu'à un peu de mal. :)
Je suis surpris que cette question ait 5 ans, et pourtant personne n'a posté plus de ce que Knuth avait à dire que quelques phrases. Les quelques paragraphes entourant la célèbre citation l'expliquent assez bien. Le document cité s'appelle " Programmation structurée avec aller aux déclarations ", et bien qu'il ait près de 40 ans, il s'agit d'une controverse et d'un mouvement logiciel qui n'existent plus tous les deux, et a des exemples dans des langages de programmation dont beaucoup de gens n'ont jamais entendu parler, une quantité étonnamment grande de ce qu'elle a dit s'applique toujours.
Voici une citation plus grande (à partir de la page 8 du pdf, page 268 dans l'original):
L'amélioration de la vitesse de l'exemple 2 à l'exemple 2a n'est que d'environ 12%, et beaucoup de gens diraient que cela est insignifiant. La sagesse conventionnelle partagée par de nombreux ingénieurs logiciels d'aujourd'hui appelle à ignorer l'efficacité dans les petits; mais je crois que c'est simplement une réaction exagérée aux abus qu'ils voient être pratiqués par des programmeurs insensés, qui ne peuvent pas déboguer ou maintenir leurs programmes "optimisés". Dans les disciplines d'ingénierie établies, une amélioration de 12%, facile à obtenir, n'est jamais considérée comme marginale; et je crois que le même point de vue devrait prévaloir en génie logiciel. Bien sûr, je ne prendrais pas la peine de faire de telles optimisations sur un travail ponctuel, mais quand il s'agit de préparer des programmes de qualité, je ne veux pas me limiter à des outils qui me refusent de telles efficacités.
Il ne fait aucun doute que le Graal de l'efficacité conduit à des abus. Les programmeurs perdent énormément de temps à penser ou à s'inquiéter de la vitesse des parties non critiques de leurs programmes, et ces tentatives d'efficacité ont en fait un fort impact négatif lorsque le débogage et la maintenance sont envisagés. 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 sera pas 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é. C'est souvent une erreur de porter un jugement a priori sur les parties d'un programme qui sont vraiment critiques, car l'expérience universelle des programmeurs qui utilisent des outils de mesure est que leurs suppositions intuitives échouent.
Un autre bon morceau de la page précédente:
Mon propre style de programmation a bien sûr changé au cours de la dernière décennie, selon les tendances de l'époque (par exemple, je ne suis plus aussi délicat et j'utilise moins de go to), mais le changement majeur de mon style est dû à ce phénomène de boucle interne. Je regarde maintenant avec un œil extrêmement jaunâtre chaque opération dans une boucle interne critique, cherchant à modifier mon programme et la structure de données (comme dans le passage de l'exemple 1 à l'exemple 2) afin que certaines opérations puissent être éliminées. Les raisons de cette approche sont les suivantes: a) cela ne prend pas longtemps, car la boucle intérieure est courte; b) le gain est réel; et c) Je peux alors me permettre d'être moins efficace dans les autres parties de mes programmes, qui sont donc plus lisibles et plus faciles à écrire et à déboguer.
J'ai souvent vu cette citation utilisée pour justifier un code manifestement mauvais ou un code qui, bien que ses performances n'aient pas été mesurées, pourrait probablement être accéléré assez facilement, sans augmenter la taille du code ou compromettre sa lisibilité.
En général, je pense que les micro-optimisations précoces peuvent être une mauvaise idée. Cependant, les macro-optimisations (des choses comme le choix d'un algorithme O (log N) au lieu de O (N ^ 2)) valent souvent la peine et devraient être faites tôt, car il peut être inutile d'écrire un algorithme O (N ^ 2) et puis jetez-le complètement en faveur d'une approche O (log N).
Notez les mots peut être: si l'algorithme O (N ^ 2) est simple et facile à écrire, vous pouvez le jeter plus tard sans trop de culpabilité s'il s'avère trop lent. Mais si les deux algorithmes sont tout aussi complexes, ou si la charge de travail attendue est si importante que vous savez déjà que vous aurez besoin du plus rapide, l'optimisation précoce est une décision d'ingénierie judicieuse qui réduira votre charge de travail totale à long terme.
Ainsi, en général, je pense que la bonne approche consiste à découvrir quelles sont vos options avant de commencer à écrire du code et à choisir consciemment le meilleur algorithme pour votre situation. Plus important encore, l'expression "l'optimisation prématurée est la racine de tout mal" n'est pas une excuse pour l'ignorance. Les développeurs de carrière devraient avoir une idée générale du coût des opérations courantes; ils devraient savoir, par exemple,
Et les développeurs doivent être familiarisés avec une boîte à outils de structures de données et d'algorithmes afin qu'ils puissent facilement utiliser les bons outils pour le travail.
Avoir beaucoup de connaissances et une boîte à outils personnelle vous permet d'optimiser presque sans effort. Mettre beaucoup d'efforts dans une optimisation qui pourrait être inutile is evil (et j'avoue être tombé dans ce piège plus d'une fois). Mais lorsque l'optimisation est aussi simple que de choisir un ensemble/une table de hachage au lieu d'un tableau, ou de stocker une liste de nombres en double [] au lieu de la chaîne [], alors pourquoi pas? Je suis peut-être en désaccord avec Knuth ici, je ne suis pas sûr, mais je pense qu'il parlait d'optimisation de bas niveau alors que je parle d'optimisation de haut niveau.
N'oubliez pas que cette citation date de 1974. En 1974, les ordinateurs étaient lents et la puissance de calcul était chère, ce qui donnait à certains développeurs une tendance à suroptimiser, ligne par ligne. Je pense que c'est contre cela que Knuth faisait pression. Il ne disait pas "ne vous inquiétez pas du tout de la performance", car en 1974, ce serait juste un discours fou. Knuth expliquait comment pour optimiser; en bref, il faut se concentrer uniquement sur les goulots d'étranglement, et avant cela, vous devez effectuer des mesures pour trouver les goulots d'étranglement.
Notez que vous ne pouvez pas trouver les goulots d'étranglement avant d'avoir écrit un programme à mesurer, ce qui signifie que certaines décisions de performance doit être prises avant que quoi que ce soit n'existe pour mesurer. Parfois, ces décisions sont difficiles à changer si vous vous trompez. Pour cette raison, il est bon d'avoir une idée générale de ce que coûtent les choses afin de pouvoir prendre des décisions raisonnables lorsqu'aucune donnée matérielle n'est disponible.
La rapidité d'optimisation et le degré de préoccupation des performances dépendent du travail. Lorsque vous écrivez des scripts que vous n'exécuterez que quelques fois, vous inquiéter des performances est généralement une perte de temps totale. Mais si vous travaillez pour Microsoft ou Oracle et que vous travaillez sur une bibliothèque que des milliers d'autres développeurs vont utiliser de milliers de façons différentes, cela peut être payant pour optimiser l'enfer, donc que vous pouvez couvrir efficacement tous les divers cas d'utilisation. Même ainsi, le besoin de performances doit toujours être mis en balance avec le besoin de lisibilité, de maintenabilité, d'élégance, d'extensibilité, etc.
Personnellement, comme indiqué dans un fil précédent , je ne pense pas que l'optimisation précoce soit mauvaise dans les situations où vous savez que vous rencontrerez des problèmes de performances. Par exemple, j'écris des logiciels de modélisation et d'analyse de surfaces, où je traite régulièrement avec des dizaines de millions d'entités. La planification des performances optimales au stade de la conception est bien supérieure à l'optimisation tardive d'une conception faible.
Une autre chose à considérer est la façon dont votre application évoluera à l'avenir. Si vous considérez que votre code aura une longue durée de vie, l'optimisation des performances au stade de la conception est également une bonne idée.
D'après mon expérience, l'optimisation tardive fournit de maigres récompenses à un prix élevé. L'optimisation au stade de la conception, par la sélection d'algorithmes et les ajustements, est bien meilleure. Selon un profileur pour comprendre comment fonctionne votre code n'est pas un excellent moyen d'obtenir du code haute performance, vous devez le savoir au préalable.
En fait, j'ai appris que la non-optimisation prématurée est plus souvent la racine de tout mal.
Lorsque les gens écrivent des logiciels, ils auront initialement des problèmes, comme l'instabilité, des fonctionnalités limitées, une mauvaise utilisation et de mauvaises performances. Tous ces éléments sont généralement résolus lorsque le logiciel arrive à maturité.
Tous ces éléments, à l'exception des performances. Personne ne semble se soucier de la performance. La raison est simple: si un logiciel tombe en panne, quelqu'un corrigera le bogue et c'est tout, si une fonctionnalité est manquante, quelqu'un l'implémentera et le fera, si le logiciel a de mauvaises performances, ce n'est dans de nombreux cas pas dû à une microoptimisation manquante, mais en raison d'une mauvaise conception et personne ne va toucher la conception du logiciel. DÉJÀ.
Regardez Bochs. C'est lent comme l'enfer. Sera-t-il un peu plus rapide? Peut-être, mais seulement de l'ordre de quelques pour cent. Il n'obtiendra jamais de performances comparables à des logiciels de virtualisation comme VMWare ou VBox ou même QEMU. Parce que c'est lent par conception!
Si le problème d'un logiciel est qu'il est lent, c'est parce qu'il est TRÈS lent et cela ne peut être résolu qu'en améliorant les performances par une multitude. + 10% ne rendra tout simplement pas un logiciel lent rapide. Et vous n'obtiendrez généralement pas plus de 10% par des optimisations ultérieures.
Donc, si les performances sont TOUTES importantes pour votre logiciel, vous devez en tenir compte dès le début, lors de sa conception, au lieu de penser "oh oui, c'est lent, mais nous pouvons l'améliorer plus tard". Parce que tu ne peux pas!
Je sais que cela ne correspond pas vraiment à votre cas spécifique, mais cela répond à la question générale "L'optimisation prématurée est-elle vraiment la racine de tout mal?" - avec un NON clair.
Chaque optimisation, comme toute fonctionnalité, etc. doit être soigneusement conçue et mise en œuvre avec soin. Et cela comprend une évaluation appropriée des coûts et des avantages. N'optimisez pas un algorithme pour économiser quelques cycles ici et là, quand il ne crée pas un gain de performance mesurable.
À titre d'exemple: vous pouvez améliorer les performances d'une fonction en l'intégrant, en économisant peut-être une poignée de cycles, mais en même temps, vous augmentez probablement la taille de votre exécutable, augmentant les chances de TLB et les échecs de cache coûtant des milliers de cycles ou même les opérations de pagination, qui tueront complètement les performances. Si vous ne comprenez pas ces choses, votre "optimisation" peut s'avérer mauvaise.
L'optimisation stupide est plus mauvaise que l'optimisation "prématurée", mais les deux sont toujours meilleures que la non-optimisation prématurée.
Il y a deux problèmes avec PO: premièrement, le temps de développement utilisé pour le travail non essentiel, qui pourrait être utilisé pour écrire plus de fonctionnalités ou corriger plus de bogues, et deuxièmement, le faux sentiment de sécurité que le code fonctionne efficacement. PO consiste souvent à optimiser le code qui ne sera pas le goulot d'étranglement, tout en ne remarquant pas le code qui le fera. Le bit "prématuré" signifie que l'optimisation est effectuée avant qu'un problème ne soit identifié à l'aide de mesures appropriées.
Donc, fondamentalement, oui, cela ressemble à une optimisation prématurée, mais je ne le soutiendrais pas nécessairement à moins qu'il n'introduise des bugs - après tout, il a été optimisé maintenant (!)
D'un point de vue différent, d'après mon expérience, la plupart des programmeurs/développeurs ne prévoient pas de réussir et le "prototype" devient presque toujours la version 1.0. J'ai une expérience de première main avec 4 produits originaux distincts dans lesquels le front-end chic, sexy et hautement fonctionnel (essentiellement l'interface utilisateur) a entraîné une adoption et un enthousiasme généralisés des utilisateurs. Dans chacun de ces produits, des problèmes de performances ont commencé à se manifester en un temps relativement court (1 à 2 ans), en particulier lorsque des clients plus grands et plus exigeants ont commencé à adopter le produit. Très vite, les performances ont dominé la liste des problèmes, bien que le développement de nouvelles fonctionnalités ait dominé la liste des priorités de la direction. Les clients sont devenus de plus en plus frustrés à mesure que chaque version ajoutait de nouvelles fonctionnalités qui sonnaient bien mais étaient presque inaccessibles en raison de problèmes de performances.
Ainsi, des défauts de conception et de mise en œuvre très fondamentaux qui étaient peu ou pas préoccupants dans le "type proto" sont devenus des obstacles majeurs au succès à long terme des produits (et des entreprises).
Votre démo client peut avoir l'air et être performante sur votre ordinateur portable avec les DOM XML, SQL Express et de nombreuses données mises en cache côté client. Le système de production plantera probablement une brûlure si vous réussissez.
En 1976, nous débattions encore des moyens optimaux de calculer une racine carrée ou de trier un grand tableau et l'adage de Don Knuth visait l'erreur de se concentrer sur l'optimisation de ce type de routine de bas niveau au début du processus de conception plutôt que de se concentrer sur la résolution du problème. puis l'optimisation des régions de code localisées.
Quand on répète l'adage comme excuse pour ne pas écrire de code efficace (C++, VB, T-SQL ou autre), ou pour ne pas concevoir correctement le magasin de données, ou pour ne pas considérer l'architecture de travail en réseau, alors IMO, ils ne font que démontrer un compréhension très superficielle de la nature réelle de notre travail. Rayon
Puisqu'il n'y a aucun problème à comprendre le code, ce cas peut être considéré comme une exception.
Mais en général, l'optimisation conduit à un code moins lisible et moins compréhensible et ne doit être appliqué qu'en cas de besoin. Un exemple simple - si vous savez que vous ne devez trier que quelques éléments - utilisez alors BubbleSort. Mais si vous pensez que les éléments pourraient augmenter et que vous ne savez pas combien, l'optimisation avec QuickSort (par exemple) n'est pas un mal, mais un must. Et cela devrait être pris en compte lors de la conception du programme.
Je crois que c'est ce que Mike Cohn appelle le `` plaquage or '' du code - c'est-à-dire passer du temps sur des choses qui pourraient être Nice mais qui ne sont pas nécessaires.
Il l'a déconseillé.
P.S. Le "placage à l'or" pourrait être une fonctionnalité de type cloches et sifflets selon les spécifications. Lorsque vous regardez le code, il prend la forme d'une optimisation inutile, de classes "à l'épreuve du temps", etc.
J'ai constaté que le problème de l'optimisation prématurée se produit principalement lors de la réécriture du code existant pour être plus rapide. Je peux voir comment cela pourrait être un problème d'écrire une optimisation alambiquée en premier lieu, mais surtout je vois une optimisation prématurée élever sa tête laide pour réparer ce qui n'est pas (connu pour être) cassé.
Et le pire exemple de cela est chaque fois que je vois quelqu'un réimplémenter des fonctionnalités d'une bibliothèque standard. C'est un drapeau rouge majeur. Par exemple, j'ai vu quelqu'un implémenter des routines personnalisées pour la manipulation de chaînes car il craignait que les commandes intégrées soient trop lentes.
Il en résulte un code plus difficile à comprendre (mauvais) et une perte de temps considérable sur un travail qui n'est probablement pas utile (mauvais).
La plupart de ceux qui adhèrent au "PMO" (la citation partielle, c'est-à-dire) disent que les optimisations doivent être basées sur des mesures et que les mesures ne peuvent être effectuées qu'à la toute fin.
D'après mon expérience du développement de grands systèmes, les tests de performances sont effectués à la toute fin, alors que le développement est presque terminé.
Si nous devions suivre les "conseils" de ces personnes, tous les systèmes seraient atrocement lents. Ils seraient également coûteux car leurs besoins en matériel sont bien plus importants que ceux initialement prévus.
J'ai longtemps préconisé de faire des tests de performances à intervalles réguliers dans le processus de développement: cela indiquera à la fois la présence de nouveau code (là où il n'y en avait pas auparavant) et l'état du code existant.
Une autre idée courante consiste à instrumenter le logiciel au niveau du bloc fonctionnel. À mesure que le système s'exécute, il collecte des informations sur les temps d'exécution des blocs fonctionnels. Lorsqu'une mise à niveau du système est effectuée, il est possible de déterminer quels blocs fonctionnels fonctionnent comme ils le faisaient dans la version précédente et ceux qui se sont détériorés. Sur l'écran d'un logiciel, les données de performance sont accessibles à partir du menu d'aide.
Découvrez ceci excellent article sur ce que le PMO pourrait ou ne pourrait pas signifier.
Je suppose que cela dépend de la façon dont vous définissez "prématuré". Rendre les fonctionnalités de bas niveau rapides lorsque vous écrivez n'est pas intrinsèquement mauvais. Je pense que c'est une mauvaise compréhension de la citation. Parfois, je pense que cette citation pourrait faire quelque chose de plus. Je ferais cependant écho aux commentaires de m_pGladiator sur la lisibilité.
La réponse est: cela dépend. Je dirai que l'efficacité est un gros problème pour certains types de travaux, tels que les requêtes de bases de données complexes. Dans de nombreux autres cas, l'ordinateur passe la majeure partie de son temps à attendre la saisie de l'utilisateur. Par conséquent, l'optimisation de la plupart des codes est au mieux une perte d'efforts et au pire contre-productive.
Dans certains cas, vous pouvez concevoir pour l'efficacité ou les performances (perçues ou réelles) - en sélectionnant un algorithme approprié ou en concevant une interface utilisateur de sorte que certaines opérations coûteuses se produisent en arrière-plan par exemple. Dans de nombreux cas, le profilage ou d'autres opérations pour déterminer les hotspots vous procureront un avantage 10/90.
Un exemple de cela que je peux décrire est le modèle de données que j'ai fait pour un système de gestion des affaires judiciaires qui contenait environ 560 tableaux. Cela a commencé normalisé ("magnifiquement normalisé", comme l'a dit le consultant d'une certaine grande entreprise) et nous n'avons dû y mettre que quatre éléments de données dénormalisées:
Une vue matérialisée pour prendre en charge un écran de recherche
Une table gérée par déclencheur pour prendre en charge un autre écran de recherche qui ne pourrait pas être fait avec une vue matérialisée.
Un tableau de rapport dénormalisé (il n'existait que parce que nous devions prendre en charge certains rapports de débit lorsqu'un projet d'entrepôt de données était mis en conserve)
Une table gérée par déclencheur pour une interface qui devait rechercher le plus récent d'un assez grand nombre d'événements disparates dans le système.
C'était (à l'époque) le plus grand projet J2EE en Australasie - bien plus de 100 ans de temps de développement - et il avait 4 éléments dénormalisés dans le schéma de la base de données, dont l'un n'y appartenait pas vraiment du tout.
L'optimisation prématurée n'est pas la racine de TOUS les maux, c'est sûr. Il présente cependant des inconvénients:
Au lieu d'une optimisation prématurée, on pourrait faire des tests de visibilité précoces, pour voir s'il existe un réel besoin d'une meilleure optimisation.