À l'époque des «bons vieux jours», lorsque nous copions des logiciels partagés sur des disquettes pour des amis, nous avons également utilisé un peu d'assemblage. Il y avait une pratique courante de «micro-optimisation», où vous regardiez et regardiez les lignes d'assemblage jusqu'à ce que vous trouviez un moyen de l'exprimer dans une instruction de moins. Il y avait même un dicton, qui était mathématiquement impossible, que " Vous pouvez toujours supprimer une instruction de plus. " Étant donné que la modification des performances d'exécution par de petits facteurs constants n'est pas un problème majeur pour (la plupart) de la programmation aujourd'hui, les programmeurs transfèrent ces micro- efforts d'optimisation ailleurs?
En d'autres termes, une meilleure pratique peut-elle être portée à un état extrême où elle n'ajoute plus rien de valeur? Et au lieu de cela, c'est perdre du temps?
Par exemple: les programmeurs perdent-ils du temps à généraliser des méthodes privées qui ne sont appelées qu'à partir d'un seul endroit? La perte de temps réduit-elle les données des cas de test? Les programmeurs sont-ils (encore) trop préoccupés par la réduction des lignes de code?
Il existe deux excellents exemples de ce que je recherche ci-dessous: (1) Passer du temps à trouver les bons noms de variables, voire à tout renommer; et (2) l'élimination de la duplication de code, même mineure et ténue.
Notez que ceci est différent de la question "Pour quoi optimisez-vous? ", Parce que je demande ce que les autres programmeurs semblent maximiser, avec la stigmatisation de ces optimisations "micro", et donc pas une utilisation productive du temps.
la source
Réponses:
Formatage du code
la source
=
et le quatrième pour les initialiseurs!J'écrivais beaucoup d'assembleur à l'époque. Ce n'est pas seulement que les compilateurs se sont améliorés, c'est que la plupart du matériel a maintenant beaucoup de logique consacrée à l'exécution de code dans le désordre. Le vrai micro-problème est la planification, la plupart des instructions informatiques prennent plusieurs horloges pour produire un résultat - et une charge mémoire qui manque de cache peut prendre plusieurs centaines! L'idée était donc de planifier d'autres instructions pour faire quelque chose d'utile, au lieu d'attendre un résultat. Et les machines modernes peuvent émettre plusieurs instructions par période d'horloge. Une fois que nous avons commencé à obtenir un matériel d'exécution hors service, j'ai constaté qu'essayer d'obtenir de grandes performances avec le codage manuel était devenu un jeu de tasses. Tout d'abord, le matériel hors service n'exécutera pas les instructions de votre commande soigneusement conçue, la nouvelle architecture HW sophistiquée a réduit la pénalité de la programmation logicielle non optimale suffisamment pour que le compilateur soit généralement à quelques pour cent de vos performances. J'ai également découvert que les compilateurs implémentaient maintenant des trucs bien connus mais générateurs de complexité, tels que le déroulement, le chargement par le bas, le pipelining du logiciel, etc. Utilisez-les tous et le nombre d'instructions d'assemblage dont vous avez besoin augmente plusieurs fois!
Encore plus important, la plupart des problèmes de performances ne concernent probablement pas les taux d'émission des instructions, mais la saisie des données dans le processeur. Comme je l'ai mentionné ci-dessus, la latence de la mémoire est maintenant de centaines de cycles, et le CPU peut exécuter plusieurs instructions par période d'horloge, donc à moins que le programme - et surtout les structures de données ne soient conçus de manière à ce que le taux d'accès au cache soit excessivement élevé, microtuning à l'instruction le niveau n'aura aucun gain. Tout comme les militaires disent que les amateurs parlent de tactique, les pros parlent de logistique. La programmation des performances représente désormais plus de 90% de la logistique (déplacement des données). Et cela est difficile à quantifier, car la gestion de la mémoire moderne a généralement plusieurs niveaux de cache et les pages de mémoire virtuelle sont gérées par une unité matérielle appelée TLB. L'alignement des adresses de bas niveau devient également important, car les transferts de données réels ne sont pas en unités d'octets, ou même 64 bits long-longs, mais ils viennent en unités de lignes de cache. Ensuite, la plupart des machines modernes ont un matériel qui essaie de prédire quelle ligne de cache manque, dont vous pourriez avoir besoin dans un proche avenir et émettre des préfixes automatiques pour les mettre dans le cache. La réalité est donc qu'avec les processeurs modernes, les modèles de performances sont si complexes qu'ils sont presque incompréhensibles. Même les simulateurs matériels détaillés ne peuvent jamais correspondre à la logique exacte des puces, donc un réglage précis est tout simplement impossible.
Il y a encore une place pour un codage manuel. Les bibliothèques mathématiques (comme par exemple la fonction exp), tout comme les opérations d'algèbre linéaire les plus importantes (comme la multiplication matricielle) sont toujours généralement codées à la main par des experts qui travaillent pour le fournisseur de matériel (c.-à-d. Intel ou AMD ou IBM), mais elles n'ont probablement besoin d'un couple de programmeurs assembleur de premier ordre par corp méga-ordinateur.
la source
float
. Pour une raison que je n'arrive pas à comprendre, beaucoup de gens pensent que c'est une bonne chose.Je passe parfois du temps (perdu?) À choisir un bon nom pour une variable ou une méthode afin qu'elle soit non seulement précisément descriptive, mais ait également un bon style linguistique.
Cela va un peu plus loin lorsque j'essaie de mettre l'ensemble des œuvres (un programme) dans le même style linguistique. Parfois mon avis change et je révise le livre. :)
Non, cela prend autant de temps. C'est plutôt rare. Mais j'aime garder la bonne grammaire dans mes programmes.
la source
Complexité temporelle. Je passe beaucoup trop de temps à optimiser les choses pour les pires performances sur une échelle qui est beaucoup plus grande que tout ce que je sais que le programme rencontrera de manière réaliste.
Je suis tout simplement trop obsessionnel pour lâcher le «mais ça pourrait grossir», même lorsque d'autres décisions de conception l'empêchent de se produire de manière réaliste.
Cependant, pour ma défense, si l'irréaliste devenait réalité .. l'optimisation n'est vraiment plus «micro». Remarque, je n'ai pas dit impossible , mais irréaliste .
Bien sûr, les exigences ont priorité.
la source
Je pense que j'ai perdu des semaines en tripotant des gestionnaires d'exceptions Java.
C'est beaucoup de travail pour le code qui échoue deux ou trois fois par an. Cela devrait-il être un avertissement ou une info? Erreur ou fatal? Est-ce vraiment fatal que le processus recommence dans cinq minutes? Est-ce vraiment un avertissement si tout est encore à l'état par défaut?
Beaucoup de nombril et de discussion sur la nature d'une erreur d'E / S. Si vous ne pouvez pas lire un fichier sur le réseau, s'agit-il d'une erreur de fichier ou d'une erreur réseau? Etc.
À un moment donné, j'ai remplacé toutes les chaînes de concaténation
String.format
afin que l'erreur de fichier annuelle n'entraîne pas la création d' objets supplémentaires dans le tas. C'était un gaspillage.la source
Je suppose que je suis trop préoccupé par les LOC. Pas tant les LOC eux-mêmes, mais plutôt le nombre de déclarations et encore plus de déclarations en double. Je suis allergique au code en double. En général, j'adore le refactoring, mais je suppose qu'environ 50% de ce que je fais ne rend pas le code nettement plus agréable.
la source
Lire un fichier ligne par ligne au lieu de simplement lire la ligne entière dans une chaîne et traiter la chaîne en une seule prise.
Bien sûr, cela fait une différence dans la vitesse d'exécution mais vaut rarement les lignes de code supplémentaires. Cela rend le code beaucoup moins facile à gérer et augmente la taille du code.
Oui, cela n'a probablement pas de sens si le fichier fait 3 Go mais la plupart des fichiers ne sont pas si gros (du moins pas ceux avec lesquels je travaille ;-)).
la source
Lorsque j'écrivais un langage d'assemblage, il était logique de taper sur les octets et les cycles, mais c'était il y a longtemps et les compilateurs ont parcouru un long chemin depuis. Je n'essaierais pas d'en optimiser manuellement un maintenant.
De même, lorsque je suis passé à l'écriture en C, il était assez facile de faire un meilleur travail que les compilateurs C, mais chaque année, Borland ou Microsoft ou quelqu'un en publiait un nouveau qui améliorait le précédent dans la salle. J'ai commencé à prêter attention au code d'assemblage réel que le compilateur émettait et, si cela n'écrivait pas du code agréable et serré, dérouler des boucles, déplacer des variables en dehors des boucles, etc.
De nos jours, j'écris dans des langages beaucoup plus élevés comme Perl, Python et Ruby. J'utilise certains des trucs du compilateur à l'avant, comme le déroulage de boucle si cela a du sens, le déplacement de variables statiques en dehors des boucles, etc., mais je ne m'en inquiète pas autant car les processeurs sont un peu plus rapides maintenant. Si une application semble se déplacer de façon inattendue, j'utiliserai un profileur et verrai ce que je peux trouver, puis si quelque chose peut être amélioré, je commencerai à comparer les différentes façons dont je peux penser pour faire quelque chose plus rapidement, puis voir comment cela peut évolue.
En général, j'essaie d'être intelligent sur la façon dont j'écris du code, basé sur des années d'expérience.
la source
Une micro-optimisation: améliorer les performances mono-thread des choses qui sont parallélisables ou qui peuvent simplement être améliorées avec un nouveau matériel.
Si quelque chose est lent sur un ordinateur il y a 10 ans, une solution moins coûteuse est probablement d'acheter un ordinateur plus récent et plus rapide, plutôt que de perdre du temps à optimiser le programmeur. Un gros objectif des optimisations envisagées devrait cependant être de trouver un moyen d'utiliser les 8 cœurs que vous pouvez obtenir aujourd'hui, ou les 64 que vous pourrez obtenir dans quelques années, au lieu de vous tourmenter à propos des minuties comme le coût supplémentaire des ordures collection.
la source
La micro-optimisation en termes de performances d'exécution n'est guère un problème fermé, même aujourd'hui (bien qu'elle puisse être moins courante). Je dois parfois expliquer aux gens que le surcoût de l'allocation d'un objet supplémentaire à chaque demande ou de l'ajout d'un appel de fonction supplémentaire est négligeable.
En dehors de cela, je vois également des programmeurs micro-optimiser pour plus de simplicité (y compris moi-même). Je n'ai pas encore travaillé quelque part où je n'ai pas eu à expliquer la différence entre simplicité et simplisme.
la source
programmers.
=programmers.stackexchange.com
différent de chaque endroit où vous avez travaillé où vous avez dû expliquer la différence entre ces deux concepts? Veuillez expliquer la différence.Je suis tout à fait pour le principe de ne pas optimiser, sauf si c'est vraiment nécessaire. Et je suis pour le principe du profil d'abord. Et en principe, je n'optimiserai que quelque chose qui fera la différence.
C'est en principe. Mais le principe est un menteur.
J'ai l'habitude de faire attention aux fonctions des bibliothèques fréquemment utilisées qui, je pense, seront beaucoup utilisées dans les boucles internes. Et puis, lorsque vous repérez quelque chose dans une autre fonction, ce n'est pas si souvent utilisé ...
Oh - et puis il y a la logique "je l'écris - bien sûr, c'est une bibliothèque importante".
Oh - et BTW. Je n'ai encore jamais utilisé de profileur pour guider une optimisation. Je n'ai pas accès à un service commercial et je n'ai jamais vraiment développé la motivation pour comprendre gprof.
En gros, je suis un hypocrite. Ces principes s'appliquent à d'autres personnes, mais j'ai trop peur que quelqu'un dise "mais pourquoi l'avez-vous écrit de cette manière lente et merdique?" donc je ne peux jamais vraiment les appliquer correctement à moi-même.
Cependant, je ne suis pas aussi mauvais que je le dis ici. Et je suis complètement immunisé contre l'auto-tromperie, alors vous savez que j'ai raison à ce sujet!
ÉDITER
Je devrais ajouter - l'un de mes principaux problèmes stupides est les frais généraux d'appel. Pas partout, bien sûr, mais dans ces cas où je pense que ça va être utilisé beaucoup dans les boucles internes (où je n'ai aucune preuve objective d'un problème). J'ai écrit du code désagréable basé sur offsetof pour éviter de faire des appels de méthode virtuelle plus d'une fois. Le résultat peut même être plus lent, car ce n'est probablement pas un style de codage que les optimiseurs sont conçus pour bien gérer - mais c'est plus intelligent ;-)
la source
Autrefois, lorsque les ordinateurs avaient des temps d'horloge mesurés en microsecondes, de la mémoire mesurée en kilo-octets et des compilateurs primitifs, la micro-optimisation avait un certain sens.
La vitesse et la taille des ordinateurs de génération actuelle et la qualité des compilateurs de génération actuelle signifient que la micro-optimisation est généralement une perte de temps totale. Les exceptions ont tendance à se produire lorsque vous devez absolument obtenir des performances maximales d'un code à forte intensité de calcul ... ou lorsque vous écrivez pour un système embarqué avec des ressources extrêmement limitées.
Twitter et Facebook me viennent à l'esprit :-)
la source