Pourquoi devrais-je me soucier de la micro performance et de l'efficacité?


71

De nombreuses questions et réponses sur les pages C / C ++, traitent spécifiquement ou indirectement de problèmes de micro performance (tels que la surcharge d’une fonction indirecte vs directe ou directe), ou l’utilisation d’un algorithme O (N 2 ) vs O (N log N) sur une liste de 100 articles.

Je code toujours sans me soucier des micro-performances, et peu de préoccupations des macro-performances, en mettant l'accent sur un code facile à maintenir et fiable, à moins que ou jusqu'à ce que je sache que j'ai un problème.

Ma question est la suivante: pourquoi un grand nombre de programmeurs se soucient-ils autant? Est-ce vraiment un problème pour la plupart des développeurs, ai-je juste eu la chance de ne pas trop m'en inquiéter ou suis-je un mauvais programmeur?


5
+1, bonne question générale.
iammilind

+1 bonne question..J'ai ajouté 2 tags .. j'espère que cela ne vous dérange pas.

2
Je dirige deux grandes citations 1) "L'optimisation prématurée est la racine de tout le mal." 2) 80% de votre temps sera consacré à 20% de votre code (règle des 80/20).
James Khoury

2
Je remarque que quelques réponses parlent de mon exemple O (n * n). J'ai explicitement spécifié une liste de 100 éléments, mais ils insistent toujours pour dire que le O (nlogn) est meilleur, indiquant explicitement les améliorations de performances si la liste, dans le futur, va à 1000 ou à des millions. Cette micro-optimisation est-elle une obsession parce que les programmeurs programment des exigences futures possibles plutôt que des exigences actuelles? (Où ai-je entendu cela auparavant ...)
Mattnz

5
@ James, la citation complète de Donald Knuth est la suivante: "Nous devrions oublier les petites efficacités, disons environ 97% du temps: l'optimisation prématurée est la racine de tous les maux". Il y aura quelques bonnes réponses sur les 3% restants dans ce fil.
StuperUser

Réponses:


14

En pratique, la performance est rarement un problème qui nécessite une gestion aussi détaillée. Il vaut la peine de garder un œil sur la situation si vous savez que vous allez stocker et manipuler d'énormes quantités de données, mais sinon, vous avez raison et il est préférable de garder les choses simples.

L'un des pièges les plus faciles à tomber - en particulier en C et C ++, où vous avez un contrôle aussi fin - est l'optimisation trop tôt et à un niveau trop fin. En règle générale, la règle est la suivante: A) n'optimisez pas jusqu'à ce que vous sachiez que vous avez un problème, et B) n'optimisez rien de ce qui ne vous a pas été révélé problématique en utilisant un profileur.

Un corollaire à B) est le suivant: les programmeurs ont la réputation de ne pas savoir où se situent leurs goulots d’étranglement, même s’ils pensent que c’est bien. Utilisez un profileur et optimisez les composants lents ou modifiez les algorithmes si une section de code est appelée trop de fois, de sorte que cela pose un problème.


6
Une autre: le code d'initialisation qui s'exécute une fois n'a généralement pas besoin d'optimisation, alors cherchez ailleurs.
Mike DeSimone

3
Dépend de combien de fois "une fois" est. Lors de l'exécution ./configure, j'oserais dire que jusqu'à 75% du temps d'exécution peut être consacré à du code "d'initialisation" dans les programmes exécutés par le script. 25 à 50% pourraient même être dépensés en liens dynamiques.
R ..

12
La règle A est une règle terrible. L'architecture d'un système joue un rôle dans les performances, et si vous découvrez plus tard que votre architecture ne peut tout simplement pas prendre en charge vos exigences de performances, vous êtes fondamentalement foutu. Ainsi, bien que vous puissiez passer au-dessus des détails les plus fins, ignorer complètement cela au début est tout simplement faux.
edA-qa mort-ora-y

3
@ edA-qa: J'avais l'habitude de le penser, mais au fil des ans, de nombreux autres projets ont échoué ou ont échoué avant que toute considération de performance ne devienne une préoccupation. Chaque fois que je rencontrais des problèmes de performances, le correctif représentait un coût comparativement faible, en jours ou en quelques semaines. Pas plus préoccupant que tout autre "bogue" détecté et correctif en développement. Cependant, comme pour tout autre élément de risque, les problèmes de performance doivent être identifiés et atténués au début du projet.
Mattnz

5
Le PO a demandé pourquoi autant d'attention, et je ne vois pas en quoi cette réponse répondait à la question, à moins que le PO ne soit plus intéressé à entendre quelqu'un dire "ne t'inquiète pas pour ça!".
terre rouge

54

Je pense que tout sur votre liste est la micro-optimisation, qui ne devrait généralement pas être examinée, sauf pour

en utilisant un algorithme O (n * n) vs O (NlogN) sur une liste de 100 articles

que je pense devrait être regardé. Bien sûr, cette liste contient 100 éléments pour le moment, et tout est rapide pour les petits n , mais je serais prêt à parier que ce même code sera réutilisé pour une liste de plusieurs millions de lignes et qu'il le sera toujours. travailler raisonnablement.

Choisir le bon algorithme n'est jamais une micro-optimisation. Vous ne savez jamais sur quels types de données le même code sera utilisé pendant deux mois ou deux ans plus tard. Contrairement aux "micro-optimisations" qui sont faciles à appliquer avec l'aide d'un profileur, les modifications apportées aux algorithmes nécessitent souvent une refonte importante pour une utilisation efficace des nouveaux algorithmes. (Par exemple, certains algorithmes exigent que les données d'entrée soient déjà triées, ce qui peut vous obliger à modifier des parties importantes de vos applications pour garantir que les données restent triées.)


36
+1 pour "Choisir le bon algorithme n'est jamais une micro-optimisation."

9
I + 1 aussi, mais notez que le choix de l'algorithme big-O-optimal lorsque vos tailles de données sont sûrement petites peut être préjudiciable au temps de développement, à la taille du programme et peut-être même à l'utilisation de la mémoire. Si vous triez les mains de poker, voulez-vous vraiment écrire un tri rapide, un smoothsort ou un mergesort? Je commencerais par un simple tri par insertion ou par un réseau de tri.
R ..

8
Ca c'est drôle. Dans un fil sur la micro-optimisation, de nombreux commentateurs micro-optimisent les réponses. ;)
Sécurisé

5
"Je serais prêt à parier que ce même code sera réutilisé pour une liste de plusieurs millions de lignes": Cela dépend complètement du domaine problématique. Exemples: si vous écrivez un algorithme d’échecs, vous pouvez être raisonnablement sûr que la taille du plateau ne changera pas. Si vous programmez un véhicule autonome, le nombre de roues ne augmentera pas aussi rapidement.
nikie

3
Je n'aime pas "choisir le bon algorithme n'est jamais une micro-optimisation" parce que c'est VRAIMENTMENT vrai, étant donné la nature du mot "correct". Cependant, j'ai l'impression que votre implication est vraiment "l'algorithme le plus rapide ou le plus efficace", ce avec quoi je ne suis pas d'accord. Choisir l'algorithme le plus efficace est le mauvais choix si sa mise en œuvre prend beaucoup de temps et que la vitesse ou l'espace de ce segment importe peu.
Casey Patton le

18

Il y a très peu de temps, dans mon premier emploi, j'avais écrit du code pour les systèmes embarqués. Ces systèmes utilisaient des microprocesseurs 8086 et avaient une mémoire limitée. Nous avons utilisé le compilateur Intel C. Un système que j'ai construit avait besoin d'accéder à un tableau 3D de structures. Je l'ai construit comme le livre me l'a dit: appelez malloc pour les 3 dimensions, puis allouez des lignes pour la dimension suivante, puis calloc pour les noeuds finaux.

C'était assez compliqué (pour moi à l'époque), je devais faire l'ajustement des courbes, le contrôle du processus ANOVA et l'analyse du chi carré. Aucune bibliothèque n’a fait cela pour nous; nous avons dû tout écrire et l’adapter au 8086.

Le système fonctionnait comme un chien. Après un rapide profilage, j'ai découvert que l'un des plus gros problèmes était l'allocateur. Pour résoudre le problème, j'ai abandonné tous les appels à malloc et procédé à ma propre gestion de la mémoire d'un gros bloc de mémoire.


Dans un autre cas concernant le même travail, le client se plaignait du temps de réponse de son système de contrôle de processus statistique. L'équipe avant moi avait conçu un système "logiciel PLC" dans lequel les opérateurs pouvaient utiliser une logique booléenne pour combiner des signaux et des commutateurs de déclenchement. Ils l'ont écrit dans un langage simplifié, ce que nous appellerions aujourd'hui un "langage spécifique à un domaine". comme je me souviens il ressemblait ((A1 + B1) > 4) AND (C1 > C2)et ainsi de suite.

La conception d'origine a analysé et interprété cette chaîne chaque fois qu'elle a été évaluée. Sur notre pauvre processeur, cela a pris beaucoup de temps et cela signifiait que le contrôleur de processus ne pouvait pas se mettre à jour aussi rapidement que le processus tournait.

J'y ai jeté un nouveau regard et ai décidé de traduire cette logique en code assembleur, au moment de l'exécution. Je l'ai analysé une fois , puis à chaque exécution, l'application a appelé une fonction générée dynamiquement. Un peu comme certains virus aujourd'hui, je suppose (mais je ne le sais pas vraiment). Le résultat a été multiplié par 100, ce qui a rendu le client et mon supérieur vraiment heureux.

Le nouveau code n'était pas aussi facile à gérer, étant donné que j'avais construit un compilateur personnalisé . Mais l’avantage de la performance l’emportait largement sur l’inconvénient de la maintenance.


Plus récemment, je travaillais sur un système qui devait analyser une mouche XML de manière dynamique. Les fichiers plus volumineux prendraient beaucoup plus de temps. C'était très sensible à la performance; trop lent d'une analyse, l'interface utilisateur deviendrait complètement inutilisable.

Ce genre de choses arrive tout le temps.


Donc, parfois, vous voulez du code facile à gérer, facile à écrire. Parfois, vous voulez un code qui s'exécute rapidement. Le compromis est la décision technique que vous devez prendre pour chaque projet.


9
Dans tous vos exemples, l’optimisation ultérieure ne coûtait pas beaucoup plus cher que d’écrire le code rapide dès le début. Donc, écrire du code plus simple et plus lent en premier, puis optimiser le cas échéant, ont bien fonctionné pour tous.
CodesInChaos

6
@CodeInChaos: La réponse ne prétend pas le contraire. Cela répond à la question du PO "Pourquoi devrais-je me préoccuper de la micro performance et de l'efficacité?" Les problèmes de pré-optimisation ont simplement été déduits par les autres répondants.
Webbiedave

12

Si vous traitez des images volumineuses et que vous parcourez chaque pixel, il peut s'avérer crucial d'ajuster les performances.


2
+1 - également, financement haute fréquence, tout type de codeur / décodeur audio / vidéo, simulations et modélisations (par exemple, jeux), bits à l'échelle du système tels que programmateurs de CPU et gestionnaires de mémoire, etc.
Billy ONeal

3
POUVEZ être critique, mais EST seulement critique une fois que vous avez prouvé qu'il en était ainsi et que vous l'avez décrit à l'endroit où vous pensez que le problème se situe. (Indice: ce n'est probablement pas là.)
JUSTE MON AVIS correct

2
@JUST MON AVIS correct: en réalité, pour le traitement des images, le traitement des données est généralement le deuxième plus gros consommateur de temps (les E / S sont toujours les plus gros). Cependant, l'optimisation pour les E / S nécessite de nombreuses conceptions inhabituelles / folles et leur acceptation par les autres programmeurs, et il est parfois impossible de les améliorer. La partie traitement, toutefois, est généralement extrêmement parallélisable et constitue donc un avantage facilement exploitable. (Les ajustements peuvent être vus par d'autres comme une implémentation de manuel
classique

12

Laissez-moi vous parler un peu du pourquoi de cette culture.

Si vous êtes plus près de 40 que de 20 et que vous programmez votre vie à l'âge adulte, vous êtes devenu majeur lorsque C ++ était vraiment le seul jeu en ville, les applications de bureau étaient la norme et le matériel était encore logiciel très en retard en termes de capacités de bande passante / performances.

  • Avant, nous devions faire des tours de programmation stupides pour pouvoir lire de gros fichiers (> 2G) ...
  • Nous nous inquiétions de la taille des exécutables ...
  • Nous avions l'habitude de nous inquiéter de la quantité de mémoire consommée par nos programmes ...
  • Nous prenons régulièrement des décisions algorithmiques en matière de compromis temps / espace ...
  • Même sur le back-end, nous devions écrire des programmes CGI en C ou C ++ pour que n'importe quoi puisse gérer un no décent. de RPS ... Il était plusieurs ordres de grandeur plus rapide.
  • Nous avions l'habitude de faire des tests sur les mérites de performance entre delphi / c ++ / vb!

Très peu de gens ont à se soucier de ces choses aujourd'hui.

Cependant, il y a 10 ans, vous deviez toujours vous inquiéter du téléchargement de votre logiciel via un modem de 56 Ko et de son exécution sur un PC âgé de 5 ans ... Vous souvenez-vous à quel point les ordinateurs étaient de mauvaise qualité en 1996? Pensez en termes de 4 Go de disque dur, un processeur 200Mhz et 128 Mo de RAM ...

Et les serveurs d'il y a 10 ans? Le serveur "nouvelle génération" de Dell a coûté 2 000 dollars, et est livré avec 2 (!) Processeurs Pentium 1Ghz, 2 Go ou Ram et un disque dur de 20 Go.

C’était simplement un jeu de balle différent , et tous ces ingénieurs «expérimentés» qui ont 10 ans d’expérience (les gars susceptibles de répondre à vos questions) ont fait leurs armes dans cet environnement.


1
Les 20 années d’expérience supplémentaires signifient également que nous avons les marques de fabrique du processus d’optimisation et que nous évitons de faire des choses qui pourraient en avoir besoin plus tard. Même raison, je ne frappe pas beaucoup le pouce avec un marteau.
Blrfl

1
Déroulement de la boucle <shudder>
red-dirt

5
Aujourd'hui, tous les enfants qui pensaient que la bande passante, le processeur et la mémoire étaient illimités trouvaient que leurs applications mobiles ne fonctionnaient pas très bien.
gbjbaanb

9

Il y a déjà 10 réponses ici et certaines sont vraiment bonnes, mais parce que c'est une bête blanche pour moi ...

l'optimisation prématurée qui prend a) prend beaucoup plus de temps qu'une solution simple Cependant, si un développeur a le choix d'utiliser un std :: map ou un std :: vector et qu'il choisit la mauvaise collection par pure ignorance pour des performances aussi mauvaises sinon pires que l'optimisation prématurée. Et si vous pouviez légèrement modifier votre code aujourd'hui, maintenir la lisibilité, garder la même complexité, mais le rendre plus efficace, le feriez-vous? Ou appelleriez-vous cela "optimisation prématurée"? Je trouve que beaucoup de gens ne pensent même pas cela d'une manière ou d'une autre.

Auparavant, j'étais le type qui conseillait une "micro-optimisation" qui nécessitait très peu de changement et la même réponse que celle que vous venez de dire: "Vous ne devez pas optimiser trop tôt. Il suffit de le faire fonctionner et nous le changerons plus tard s'il y a un problème de performance ". Il a fallu plusieurs versions avant de le réparer. Et oui c'était un problème de performance.

Bien que l'optimisation initiale puisse ne pas être bonne, je pense qu'il est très bénéfique que les gens écrivent du code avec une compréhension de ce que ce code va faire et n'ignorent simplement pas les questions résultant de la notation O (x) en tant que "optimisation". Vous pouvez écrire beaucoup de code maintenant et, en réfléchissant un peu aux performances, évitez 80% des problèmes à venir.

Considérez également que de nombreux problèmes de performances ne vont pas se produire dans votre environnement et pas tout de suite. Il arrive parfois qu'un client repousse les limites ou qu'un autre développeur décide de créer au-dessus de votre infrastructure et d'augmenter le nombre d'objets de 10 fois. Avec certaines performances actuelles, vous pourriez éviter une nouvelle conception très coûteuse plus tard. Et si le problème est détecté après la publication officielle du logiciel, même un correctif simple devient 20 fois plus coûteux à appliquer.

En conclusion, garder toujours à l’esprit la performance aide à développer de bonnes habitudes. Ce qui est tout aussi important d’avoir une écriture propre, aussi simple que possible et organisée.


+1: C’est l’un des facteurs d’employabilité des personnes embauchées pour développer le logiciel Shrinkwrap et les sites Web commerciaux . La prévention coûte moins cher que la malédiction du client.
Rwong

6

Je soupçonne qu’une grande partie de ce que vous constatez est une simple erreur d’échantillonnage. Lorsque les gens traitent avec des situations simples, ils écrivent du code et c'est la fin des choses. Ils posent des questions lorsqu'ils traitent de problèmes relativement délicats, tels que la nécessité d'optimiser, en particulier dans une situation où l'optimisation n'est pas nécessairement évidente.

Cela dit, il y a sans aucun doute une optimisation prématurée en jeu également. Correctement ou non, C et C ++ ont la réputation d'être performants, ce qui a tendance à attirer les gens qui se soucient de la performance, y compris ceux qui peuvent faire de l'optimisation autant par plaisir que par nécessité.


1
+1 - Remarque: la plupart des questions SO avec la balise "performance" font probablement partie de cette erreur d'échantillonnage: P
Billy ONeal,

3
Je vois bien sûr un tas de questions d’optimisation prématurée ici… Je pense que cela vient du fait que beaucoup de programmeurs amateurs ont commencé à écrire des jeux, et qu’il existe un énorme corpus de livres «d’optimisation» absurdes et sites Web liés au développement de jeux qui mettent de mauvaises idées dans la tête des débutants. :-)
R ..

4
Lorsque vous avez affaire à quelque chose de délicat, il semble souvent plus facile de faire une pause dans le problème épineux et de perdre du temps à vous soucier de savoir si vous devriez utiliser i++ou non++i
Carson63000

@ Carson63000: oui, cela pourrait fausser totalement les échantillons. Ou alors, ils passent du temps à répondre aux questions sur la raison pour laquelle ma operator ++compilation n'a pas été effectuée.
Rwong

4

Quelques autres réponses mentionnent les systèmes embarqués , et j'aimerais développer davantage.

Il existe de nombreux dispositifs contenant des processeurs bas de gamme, par exemple: le contrôleur de chaudière de votre maison, une simple calculatrice de poche ou les dizaines de puces dans une voiture moderne.

Pour économiser de l'argent, ceux-ci peuvent avoir des quantités de mémoire flash (pour stocker du code) et de RAM qui semblent minuscules à ceux qui n'ont écrit que du code pour PC ou smartphones. Pour économiser de l'énergie, ils peuvent fonctionner à une fréquence d'horloge relativement faible.

Pour prendre un exemple, la famille de microcontrôleurs STM32 va de 24 MHz, 16 Ko en mémoire flash, 4 Ko de RAM , jusqu'à 120 MHz, 1 Mo en mémoire flash et 128 Ko de RAM .

Lorsque vous écrivez du code pour des puces comme celles-ci, vous gagnez beaucoup de temps si vous souhaitez que votre code soit aussi efficace que possible. De toute évidence, l'optimisation prématurée reste une mauvaise idée; mais avec de la pratique, vous apprendrez comment des problèmes courants peuvent être résolus rapidement et / ou avec un minimum de ressources, et codez en conséquence.


1
bons points à considérer pour les systèmes embarqués, un domaine dans lequel je travaille moi-même. Même avec cela à l'esprit, mon expérience au fil des ans est que l'optimisation erronée est toujours une perte de temps. Sans outils pour nous guider, nous trouvons rarement les zones à problèmes
Jeff

2

Il s’agit essentiellement de langages de bas niveau. Lorsque l’on se heurte à un problème de performances pathologiques où un détail sans importance est à l’origine du goulot d’étranglement, on a en fait l’occasion de contourner le problème (plutôt qu'avec la plupart des autres). langues); mais bien sûr, souvent, comment le faire plus efficacement n’est pas immédiatement évident. D'où la moitié des questions de micro-optimisation étranges / intéressantes posées ici.

L'autre moitié vient de ceux qui sont curieux de savoir à quel point ils peuvent se rapprocher du métal. Ce sont essentiellement des langages de bas niveau, après tout ...


+1: il est intéressant de noter que la "performance pathologique" peut arriver à n'importe qui dans le monde, indépendamment de la langue ou de la plateforme. La possibilité de ré-implémenter dans un langage de niveau inférieur pour les tests et le désassemblage de lecture peut fournir plus d' informations , mais ne fournit pas toujours une solution viable. Exemple: "Je sais que je peux le faire en assemblage, mais il doit fonctionner dans un environnement de confiance partielle!"
Rwong

2

Les performances sont toujours un sujet brûlant lorsque vous traitez en C et C ++. En ce qui concerne la distance à parcourir, vous pouvez toujours devenir fou au point d’intégrer l’ASM ou d’utiliser l’arithmétique de pointeur pour une itération plus rapide. Cependant, il arrive un moment où l’on consacre tellement de temps à optimiser que l’élaboration du programme global s’arrête.

Quand on traite de ces problèmes, il y a la performance du programmeur et celle du code. Laquelle de ces questions sur lesquelles on se concentrera posera toujours des questions intéressantes. En fin de compte, la question la plus importante est de savoir à quel point cela est perceptible pour l'utilisateur. L'utilisateur travaillera-t-il avec des données qui créent des tableaux avec des centaines ou des milliers d'éléments? Dans ce cas, le codage permettant d'accomplir des tâches rapidement peut amener l'utilisateur à se plaindre de la lenteur des opérations standard du programme.

Ensuite, il y a l'utilisateur qui travaillera avec de petites quantités de données. Quelques fichiers ici et là, où des opérations telles que le tri et les opérations sur les fichiers ne seront pas aussi perceptibles par l'utilisateur si vous utilisez des fonctions de niveau supérieur qui facilitent la maintenance au détriment de certaines performances.

Ceci est juste un petit exemple des problèmes que vous rencontrerez. Les autres problèmes incluent le matériel de l'utilisateur cible. Vous devrez vous soucier davantage de la performance si vous utilisez des systèmes embarqués, puis si vos utilisateurs disposent, par exemple, de machines à double cœur avec des concerts de RAM.


Hmm .. Je n'utilise pas l'arithmétique de pointeur pour une itération plus rapide - il s'agit d'une multiplication et ajout d'instructions par boucle, que vous utilisiez une itération basée sur un index ou une autre. Je l'utilise cependant, car elle est généralement plus claire que l'itération basée sur l'index.
Billy ONeal

l'arithmétique de pointeur n'est pas plus rapide que w / e.

2

Pourquoi les programmeurs se soucient-ils autant? Il y a des idées stupides qui peuplent leur esprit, telles que la résolution de problèmes de performance avant qu’ils ne sachent qu’ils les ont, et qu’ils ne comprennent pas quand ils devinent .

C'est délicat car, selon mon expérience, il y a des problèmes de performances auxquels il faut penser à l'avance. Il faut de l'expérience pour savoir ce qu'ils sont.

Cela dit, la méthode que j'utilise est semblable à la vôtre, mais pas la même.

  1. Commencez avec la conception la plus simple possible. En particulier, la structure des données doit être aussi normalisée et minimale que possible. Dans la mesure où il y a une redondance inévitable, il convient d'éviter les notifications afin de les maintenir cohérentes. Il est préférable de tolérer une incohérence temporaire et de la réparer avec un processus périodique.

  2. Lorsque le programme est en cours de développement, effectuez régulièrement des réglages de performances, car les problèmes de performances ont tendance à s’infiltrer. La méthode que j’utilise est la pause aléatoire , car je pense que c’est la meilleure solution .

Voici un exemple au coup par coup de ce que je veux dire.


1

Pour être honnête, cela dépend de votre objectif et de la programmation que vous programmez de manière professionnelle ou personnelle.

De nos jours, les ordinateurs modernes sont des machines vraiment puissantes. Quelles que soient les opérations de base que vous décidez de faire, que vous essayiez ou non d'optimiser la micro-optimisation, elles peuvent effectuer leur travail de manière remarquablement rapide. Bien entendu, si vous faites autre chose (par exemple, la superinformatique pour des domaines tels que la physique ou la chimie), vous souhaiterez peut-être optimiser autant que vous le souhaitez.

Les premiers programmeurs du MIT n'étaient pas nés pour faire des choses géniales; Ils ont commencé à simplifier et à alimenter les algorithmes existants. Leur fierté était de faire en sorte que 2 + 2 donne quatre en deux secondes de moins que l'algorithme existant (ce n'est qu'un exemple, vous voyez l'idée). Ils ont constamment essayé d’utiliser moins de cartes perforées sur leurs TI-83 pour améliorer leurs performances.

De plus, si vous programmez des systèmes embarqués, vous devez certainement surveiller les performances micro. Vous ne voulez pas avoir une horloge numérique lente qui coche une seconde 5 nanosecondes plus tôt qu'une autre horloge numérique.

Enfin, si vous êtes un programmeur amateur, l'optimisation des moindres détails n'a aucun problème, même si votre programme est rapide, il l'est. Ce n'est pas nécessaire, mais vous pouvez y travailler et en profiter pour en apprendre davantage. Si vous travaillez de manière professionnelle dans un logiciel, vous ne pouvez prendre ce luxe que si cela est absolument nécessaire.


1
Je ne pense pas que le fait d'être un programmeur amateur a quelque chose à voir avec cela. Ce n'est pas parce que vous ne faites pas quelque chose de professionnel que vous avez tout le temps du monde pour le dépenser. En outre, la plupart des amateurs vont commettre de plus grosses erreurs, telles que choisir les mauvais algorithmes, que la plupart des vrais professionnels ne le feront. De plus, le professionnel travaille probablement sur un produit qui traite beaucoup plus de données que le passionné (qui doit donc être plus rapide) et doit satisfaire les clients avec les performances de l'application. Les amateurs n'ont pas de telles contraintes.
Billy ONeal

Ils ne le font pas, mais ils ont certainement plus de temps pour travailler sur eux simplement s'ils le souhaitent.

3
Je dirais le contraire. J'ai 8 heures ou plus par jour à travailler sur quelque chose en tant que professionnel. Je reçois 1, peut-être 2 heures par jour pour mes projets de loisir.
Billy Oneal,

1

en utilisant un algorithme O (N2) vs O (NlogN) sur une liste de 100 éléments.

J'étais dans une situation similaire récemment. J'ai eu un tableau d'éléments. Dans le cas attendu, il y avait deux (!) Éléments dans la liste, et même dans le pire des cas, je ne m'attends pas à plus de quatre, voire huit.

Je devais trier cette liste. En fin de compte, remplacer std::sortpar un réseau de tri (essentiellement un grand nombre de ifs imbriqués ) réduisait un pourcentage important du temps d'exécution (je ne me souviens plus du nombre, mais il était de 10 à 20%). C'est un avantage énorme d'une micro-optimisation, et le code est absolument essentiel à la performance.

Bien sûr, je ne l'ai fait qu'après le profilage. Mais le fait est que si j’utilise un langage aussi peu pratique et aussi compliqué que le C ++ (sans parler de ses règles extrêmement complexes en matière de résolution des surcharges), je souhaite en retirer tous les avantages.


1

Consommation d'énergie cumulée

Il y a une réponse que je pense toujours qui manque dans cette discussion et qui me dérange un peu - la consommation d'énergie cumulative .

Bien sûr, peu importe si vous écrivez votre programme dans un langage interprété de haut niveau et le laissez s’exécuter dans un navigateur avec quelques couches d’indirection, ou si votre boucle prend 0,01 seconde au lieu de 0,001 seconde. Personne ne le remarquera, c'est-à-dire qu'aucun utilisateur ne le remarquera.

Mais lorsque des dizaines, voire des millions d’utilisateurs utilisent parfois votre code, toute cette inefficacité supplémentaire s’ajoute. Si votre outil empêche un processeur d'entrer dans l'état de veille pendant seulement dix secondes par jour et qu'un million d'utilisateurs l'utilisent, votre algorithme inefficace vient d'utiliser 140 kWh supplémentaires [1] par jour.

Je vois rarement cela discuté et je pense que c'est triste. Je soupçonne fortement que les chiffres sont bien pires pour les frameworks populaires, tels que Firefox, et les applications Web interactives sophistiquées, et il serait intéressant de faire une recherche.


[1] Je viens de raconter cela, 10 millions de secondes, 50 watts. Le chiffre exact dépend de beaucoup de choses.


1
Vous devriez commencer par mentionner le mot magique "mobile". Lorsqu'elle est exécutée sur une plate-forme de bureau, une application nécessitant 1 / 100s de temps CPU pour dessiner une image 60 fois par seconde sera "assez rapide"; améliorer les performances dix fois plus ne ferait aucune différence pour l'utilisateur. Sur une plate-forme mobile, cependant, une application fonctionnant à 90% de l'utilisation du processeur risque de fatiguer des batteries beaucoup plus rapidement que de fonctionner à 10%.
Supercat

1

Parfois, vous avez juste des algorithmes qui ne peuvent pas être meilleurs que le temps linéaire pour lesquels il existe toujours une forte demande de performances.

Un exemple est le traitement vidéo où vous ne pouvez pas rendre une image / image plus claire en tant qu'exemple de base sans parcourir en boucle chaque pixel (eh bien, je suppose que vous pouvez utiliser une sorte de structure hiérarchique indiquant les propriétés héritées par les enfants qui finissent par descendre dans des mosaïques d'image. pour les nœuds feuilles, mais vous reporteriez alors un coût plus élevé de boucle sur chaque pixel pour le rendu et le code serait probablement plus difficile à maintenir que même le filtre d'image le plus micro-optimisé).

Il y a beaucoup de cas comme ça dans mon domaine. J'ai tendance à faire plus de boucles de complexité linéaire qui doivent tout toucher ou tout lire que celles qui bénéficient de tout type de structure de données ou d'algorithme sophistiqué. Il n'y a pas de travail qui peut être sauté quand tout doit être touché. Donc, à ce stade, si vous êtes inévitablement confronté à une complexité linéaire, vous devez rendre le travail effectué par itération moins cher et moins cher.

Donc, dans mon cas, les optimisations les plus importantes et les plus courantes sont souvent les représentations de données et les dispositions de mémoire, le multithreading et le SIMD (généralement dans cet ordre, la représentation des données étant la plus importante, car elle affecte la possibilité d'effectuer les deux dernières). Je ne rencontre pas tant de problèmes qui sont résolus par des arbres, des tables de hachage, des algorithmes de tri, etc. Mon code quotidien est plus dans la veine de "pour chaque chose, faire quelque chose."

Bien sûr, c’est un autre cas à prendre en compte lorsque des optimisations sont nécessaires (et plus important encore, lorsqu'elles ne le sont pas), micro ou algorithmiques. Mais dans mon cas particulier, si un chemin d’exécution critique doit être optimisé, les gains de vitesse 10x + sont souvent obtenus par des optimisations au niveau micro, telles que le multithreading, le SIMD et la réorganisation des configurations de mémoire et des modèles d’accès pour améliorer la localité de référence. Par exemple, il m'est moins fréquent de remplacer un type de bulle par un type introsort, un type de base ou une détection de collision à complexité quadratique par un BVH, que de rechercher des points chauds bénéficiant, par exemple, du découpage en champs chaud / froid.

Maintenant, dans mon cas, mon domaine est tellement critique en termes de performances (lancer de rayons, moteurs physiques, etc.) qu’un traceur de rayons lent mais parfaitement correct nécessitant 10 heures pour restituer une image est souvent considéré comme inutile ou plus qu'un rapide qui est complètement interactif, mais génère les images les plus laides avec des rayons qui fuient partout en raison de l'absence d'intersection rayon / tri étanche. La vitesse est sans doute la principale mesure de qualité d'un tel logiciel, sans doute même plus que la correction à un moment donné (puisque la "correction" est une idée floue avec le lancer de rayons puisque tout se rapproche, du moment qu'il ne s'écrase pas ou quelque chose comme ça). Et lorsque c'est le cas, si je ne pense pas à l'efficacité dès le départ, je me trouve dans l'obligation de modifier le code au niveau de conception le plus coûteux pour gérer des conceptions plus efficaces. Donc si je ne le fais pas

Le jeu est un autre domaine similaire au mien. Peu importe si votre logique de jeu est correcte ou si votre base de code est maintenable et brillamment conçue, si votre jeu fonctionne à 1 image par seconde, comme un diaporama. Dans certains domaines, le manque de rapidité pourrait en réalité rendre l'application inutilisable pour ses utilisateurs. Contrairement aux jeux, il n'existe pas de métrique "assez bonne" dans des domaines tels que le lancer de rayons. Les utilisateurs veulent toujours plus de vitesse et la concurrence industrielle cherche principalement à trouver des solutions plus rapides. Ce ne sera jamais assez bon jusqu'à ce que ce soit en temps réel, à quel moment les jeux utiliseront des traceurs de chemins. Et puis, cela ne sera probablement toujours pas suffisant pour les effets spéciaux, car les artistes voudront peut-être charger des milliards de polygones et effectuer des simulations de particules avec auto-collision entre des milliards de particules à plus de 30 FPS.

Maintenant, si cela peut vous rassurer, malgré tout, j’écris environ 90% du code dans un langage de script (Lua) sans me soucier de la performance. Mais j'ai une quantité inhabituellement importante de code qui doit parcourir des millions, voire des milliards de choses, et lorsque vous passez en revue des millions, voire des milliards de choses, vous commencez à remarquer une différence épique entre le code naïf à un seul thread: invoque un cache cache à chaque itération, par opposition à un code vectorisé s'exécutant en parallèle et accédant à des blocs contigus dans lesquels aucune donnée non pertinente n'est chargée dans une ligne de cache.


0

Comme vous l'avez mentionné, il est inutile de se préoccuper des problèmes de performance micro avant de prendre en compte certains problèmes réellement causés par ces problèmes.


0

Il est vraiment impossible de répondre à cette question en général. La plupart des logiciels en cours de construction sont des sites Web internes et des applications métier. Pour ce type de programmation, votre raisonnement est tout à fait correct. D'un autre côté, si vous écrivez quelque chose comme un pilote de périphérique ou un moteur de jeu, aucune optimisation n'est "prématurée"; il est probable que votre logiciel fonctionnera sur des systèmes très différents avec des contraintes matérielles différentes. Dans ce cas, vous devez optimiser les performances et vous assurer de ne pas choisir un algorithme sous-optimal.


Exactement ce que je voulais dire. Chaque logiciel a son domaine d'application et ne devrait pas se comporter de manière optimale en dehors de celui-ci. En ce sens, l'optimisation prématurée est un exemple de perfectionnisme malavisé.
K.Steff

0

Je pense que le problème du programmeur, qui se préoccupe tellement de la performance, est que parfois, dans sa vie, il devait écrire un code extrêmement performant, peut-être de toute urgence, et il a appris, appris, appris et finalement beaucoup de choses et astuces.

Et maintenant, il est difficile d’oublier, et sans mesure préalable, ce qui montre qu’il n’a pas à s’inquiéter, il est du côté de la sécurité, utilisant un code rapide.

Il est toujours agréable de montrer vos connaissances approfondies, vos compétences et quelques astuces, et de réutiliser quelque chose que vous avez appris. Vous vous sentez précieux et le temps que vous passez à apprendre vaut la peine.

Parfois, dans ma vie, j'ai appris que l'incrément de préfixe est plus rapide ...

for (int i = 0; i < MAX; ++i)

... que l'incrément postfix:

for (int i = 0; i < MAX; i++)

Maintenant, si MAX est bas, ce ne sera pas grave, et s'il y a du vrai travail dans la boucle, ce ne sera pas grave non plus. Mais il n’ya aucune raison d’utiliser la version postfix, même si aujourd’hui le compilateur optimise lui-même le code.

Peut-être que ceux qui recherchent la performance ont besoin d'un objectif supplémentaire, en plus de l'écriture de «code de travail», tel que «code de travail et lisible», pour avoir une ligne de conduite dans le grand océan d'options.


0

Ai-je juste eu la chance de ne pas trop m'en inquiéter ou suis-je un mauvais programmeur?

Vous souciez-vous de vos besoins? Si les performances ne sont pas une exigence, ne vous inquiétez pas. Consacrer du temps à cela est un mauvais service pour votre employeur.

Dans une certaine mesure, la performance est toujours une exigence. Si vous pouvez le frapper sans y penser, vous êtes en droit de ne pas y penser.

Personnellement, je suis le plus souvent motivé par la performance lorsque mes tests sont trop longs à passer. Je suis trop impatient d'attendre 5 minutes pendant qu'une série de tests réussissent. Mais cela est généralement résolu en manipulant les tests.

Ma question est la suivante: pourquoi un grand nombre de programmeurs se soucient-ils autant? Est-ce vraiment un problème pour la plupart des développeurs,

Il y a un grand nombre de programmeurs qui sont justifiés dans combien ils se soucient. Il y en a beaucoup qui ne le sont pas. Parlons de ceux qui ne le sont pas.

L’une des premières choses que les programmeurs apprennent à l’école, après avoir fait fonctionner les choses, est la grande notation O. Bon nombre d’entre eux apprennent la leçon correctement et se concentrent donc correctement sur des choses radicalement touchées par n. D'autres ne comprennent pas les calculs et ne retiennent que la leçon selon laquelle, une fois que cela fonctionne, il faut être rapide. Pire encore, certains de ces étudiants n'apprennent jamais rien de ce qui est important à faire avec votre code, à part le faire fonctionner rapidement. Les leçons manquées: rendez-les lisibles, concevez-les bien, n'y jouez pas sans raison.

Knuth avait raison: l'optimisation prématurée est la racine de tout mal. Mais une fois que cela fonctionne, quelle est la prochaine étape? Rapide non? NON! La prochaine étape est lisible. Lisible est la première, la prochaine, la seconde et la deuxième étape. La plupart des gens que je trouve en train d’optimiser des performances inutiles jettent la lisibilité sous le bus.

Certains ressentent même un frisson pervers dû à leur code illisible. Ils ont du mal à comprendre le code créé par d’autres, alors c’est maintenant à leur tour de payer.

Je le sais parce que je le faisais auparavant. Une fois, j'ai refactoré une ligne parfaitement lisible si la structure était réduite à une expression booléenne d'une ligne indéchiffrable et je l'ai fièrement envoyée à mon professeur dans l'espoir de faire bonne impression, car je pouvais créer quelque chose d'aussi compact et intimidant. Je n'ai pas eu l'éloge que j'espérais.

Si le code reste lisible, le rendre rapide plus tard est facile. C'est pourquoi Knuth insiste sur le terme "prématuré" et non "inutile". Parce que bien sûr, mieux c'est, plus vite. Mais mieux vaut mieux que dépendre de ce que vous sacrifiez pour cela. Attendez donc de savoir de quelle performance vous avez vraiment besoin avant de faire des sacrifices pour cela. Sacrifiez à contrecœur la lisibilité, car une fois qu'il est parti, il est difficile de revenir.

Au-delà de la lisibilité, le monde entier de la conception de logiciels. De quoi parle ce site Certains n'ont aucune idée de ce qu'il faut faire en matière de design. Donc, comme ils ne peuvent pas impressionner par le design, ils créent un gâchis indéchiffrable afin que les gens ne puissent pas dire qu'ils n'en ont aucune idée. Puisque personne ne répare jamais leur code, ce doit être un bon code, non?

Pour certains, la performance est l'excuse idéale pour faire ce qu'ils veulent. Les programmeurs ont beaucoup de pouvoir et d’autonomie. La confiance a été mise en eux. Ne pas abuser de la confiance.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.