Quelle est la pessimisation la plus ridicule que vous ayez vue? [fermé]


145

Nous savons tous que l'optimisation prématurée est la racine de tout mal car elle conduit à un code illisible / non maintenable. La pessimisation est encore pire, quand quelqu'un met en œuvre une «optimisation» parce qu'il pense que ce sera plus rapide, mais que cela finit par être plus lent, en plus d'être bogué, impossible à maintenir, etc. Quel est l'exemple le plus ridicule que vous ayez vu ?


21
«Pessimisation» est un mot génial.
mqp

Juste au cas où vous ne le sauriez pas, ils ont parlé de votre fil ici sur le dernier podcast.
mmcdole

Réponses:


81

Sur un ancien projet, nous avons hérité de certains (par ailleurs excellents) programmeurs de systèmes embarqués qui avaient une énorme expérience du Z-8000.

Notre nouvel environnement était Sparc Solaris 32 bits.

Un des gars est allé et a changé tous les entiers en shorts pour accélérer notre code, car saisir 16 bits de la RAM était plus rapide que saisir 32 bits.

J'ai dû écrire un programme de démonstration pour montrer que saisir des valeurs 32 bits sur un système 32 bits était plus rapide que saisir des valeurs 16 bits, et expliquer que pour saisir une valeur 16 bits, le processeur devait faire un 32 bits de large accès à la mémoire, puis masque ou décale les bits non nécessaires pour la valeur 16 bits.


16
Hé, où as-tu appris tes maths? 2 instructions avec 1 accès cache / RAM est évidemment plus rapide qu'une instruction avec 1 accès cache / RAM!
Razor Storm

2
@RazorStorm Sur les machines plus récentes où la bande passante et le cache sont plus précieux, le contraire serait vrai. Le bitmask / shift est bon marché, mais vous voulez en placer autant que possible dans le cache et minimiser la bande passante.
Jed

206

Je pense que l'expression "l'optimisation prématurée est la racine de tout le mal" est beaucoup trop utilisée. Pour de nombreux projets, c'est devenu une excuse pour ne pas prendre en compte la performance jusqu'à la fin d'un projet.

Cette phrase est souvent une béquille pour que les gens évitent le travail. Je vois cette phrase utilisée quand les gens devraient vraiment dire "Gee, nous n'avons vraiment pas pensé à cela d'avance et n'avons pas le temps de nous en occuper maintenant".

J'ai vu beaucoup plus d'exemples "ridicules" de problèmes de performances stupides que d'exemples de problèmes introduits en raison de la "pessimisation"

  • Lire la même clé de registre des milliers (ou des dizaines de milliers) de fois lors du lancement du programme.
  • Chargement de la même DLL des centaines ou des milliers de fois
  • Gaspiller des méga octets de mémoire en conservant inutilement des chemins complets vers les fichiers
  • Ne pas organiser les structures de données afin qu'elles occupent bien plus de mémoire que nécessaire
  • Dimensionnement de toutes les chaînes qui stockent les noms de fichiers ou les chemins vers MAX_PATH
  • Interrogation gratuite pour ce qui a des événements, des rappels ou d'autres mécanismes de notification

Ce que je pense être une meilleure affirmation est la suivante: "l'optimisation sans mesure ni compréhension n'est pas du tout une optimisation - c'est juste un changement aléatoire".

Un travail de bonne performance prend du temps - souvent plus que le développement de la fonctionnalité ou du composant lui-même.


46
«Prématuré» est le mot clé de cette citation. Votre reformulation en «optimisation sans mesure ni compréhension» ne semble pas changer le sens du tout. C'est précisément ce que voulait dire Knuth.
Bill the Lizard

13
@Foredecker: à droite. Trop de gens oublient le contexte, ce qui met cette citation fermement contre la micro- optimisation. Analyser un problème pour choisir le bon algorithme avant de l'implémenter n'est pas prématuré, mais trop souvent, cette citation est lancée pour justifier la solution la plus paresseuse et la plus inefficace.
Shog9

5
Cela dépend vraiment du cas individuel, il y a plus de cas où l'optimisation prématurée devient un problème, qu'une planification d'optimisation inadéquate devient un problème
Mark Rogers

12
-1: Il y a une différence entre "optimisation" et une conception appropriée. Pour ceux qui ne peuvent pas le dire, une bonne règle de base est qu'une «optimisation» rend le code plus difficile à lire, mais plus rapide ou plus efficace. Une meilleure conception rendra le code plus facile à lire (ou du moins pas pire) et plus efficace.
TED du

5
S'il est trop utilisé, alors la population qui pose des questions sur le SO est fortement pondérée vers les valeurs aberrantes. : D
dkretz

114

Les bases de données sont un terrain de jeu de pessimisation.

Les favoris incluent:

  • Divisez une table en multiples (par plage de dates, plage alphabétique, etc.) car elle est "trop ​​grande".
  • Créez une table d'archive pour les enregistrements retirés, mais continuez à la UNION avec la table de production.
  • Dupliquer des bases de données entières par (division / client / produit / etc.)
  • Résistez à l'ajout de colonnes à un index car cela le rend trop grand.
  • Créez de nombreux tableaux récapitulatifs car le recalcul à partir des données brutes est trop lent.
  • Créez des colonnes avec des sous-champs pour économiser de l'espace.
  • Dénormaliser en champs en tant que tableau.

Cela me vient à l'esprit.


Résister au besoin d'indexation est tout simplement douloureux à penser.
Bill the Lizard

2
Oui, je connais quelqu'un qui travaille pour une grande compagnie pétrolière américaine où presque toutes ses tables ont une table d'archive associée, et la plupart des requêtes sélectionnent parmi les vues qui UNION les paires de tables. La performance est comme vous vous attendez!
Tony Andrews

Hah, je pense que chaque DBA a dû abandonner l'union avec la route de la table d'archive à un moment donné. Cela semble toujours si raisonnable à l'époque.
Cruachan

3
J'ajoute: diviser la base de données en plusieurs bases de données différentes (clients ac, clients df, etc.)
Gabriele D'Antona

Pourriez-vous élaborer sur "Denormalize into fields-as-an-array."? Que voulez-vous dire ici?
Bart van Heukelom le

87

Je pense qu'il n'y a pas de règle absolue: certaines choses sont mieux optimisées dès le départ, d'autres non.

Par exemple, j'ai travaillé dans une entreprise où nous recevions des paquets de données de satellites. Chaque paquet coûtait beaucoup d'argent, donc toutes les données étaient hautement optimisées (c'est-à-dire emballées). Par exemple, la latitude / longitude n'a pas été envoyée sous forme de valeurs absolues (flottants), mais sous forme de décalages par rapport au coin "nord-ouest" d'une zone "actuelle". Nous avons dû décompresser toutes les données avant de pouvoir les utiliser. Mais je pense que ce n'est pas de la pessimisation, c'est une optimisation intelligente pour réduire les coûts de communication.

D'un autre côté, nos architectes logiciels ont décidé que les données décompressées devraient être formatées dans un document XML très lisible, et stockées dans notre base de données en tant que tel (au lieu d'avoir chaque champ stocké dans une colonne correspondante). Leur idée était que "XML est l'avenir", "l'espace disque est bon marché" et "le processeur est bon marché", il n'y avait donc pas besoin d'optimiser quoi que ce soit. Le résultat a été que nos paquets de 16 octets ont été transformés en documents de 2 Ko stockés dans une colonne, et même pour des requêtes simples, nous avons dû charger des mégaoctets de documents XML en mémoire! Nous avons reçu plus de 50 paquets par seconde, vous pouvez donc imaginer à quel point la performance est devenue horrible (BTW, la société a fait faillite).

Encore une fois, il n'y a pas de règle absolue. Oui, parfois l'optimisation trop tôt est une erreur. Mais parfois, la devise "CPU / espace disque / mémoire est bon marché" est la véritable racine de tout mal.


37
Je suis d'accord que "cpu / espace disque / mémoire est bon marché" est la véritable racine de tout mal. +1
ksuralta

5
J'ai aussi entendu ce bruit XML. Une autre compagnie tankée.
n8wrl

19
@ksuralta: "Cpu / espace disque / mémoire est bon marché" est une excuse pratique pour éviter toute réflexion. Éviter la pensée est la racine imaginaire de tout mal.
Piskvor a quitté le bâtiment

Cette XMLisation s'est également produite sur mon lieu de travail, suivie de JSONization. Tout cela pour éviter une conception de base de données relationnelle «laborieuse».
Tanz87

75

Oh mon Dieu, je pense que je les ai tous vus. Le plus souvent, il s'agit d'un effort pour résoudre les problèmes de performances par quelqu'un qui est trop paresseux pour dépanner leur chemin jusqu'à la CAUSE de ces problèmes de performances ou même rechercher s'il existe réellement un problème de performances. Dans beaucoup de ces cas, je me demande si ce n'est pas seulement le cas de cette personne qui veut essayer une technologie particulière et cherche désespérément un clou qui correspond à son nouveau marteau brillant.

Voici un exemple récent:

L'architecte de données me propose une proposition élaborée de partitionner verticalement une table de clés dans une application assez grande et complexe. Il veut savoir quel type d'effort de développement serait nécessaire pour s'adapter au changement. La conversation s'est déroulée comme suit:

Moi: Pourquoi envisagez-vous cela? Quel est le problème que vous essayez de résoudre?

Lui: table X est trop large, nous la partitionnons pour des raisons de performances.

Moi: Qu'est - ce qui vous fait penser que c'est trop large?

Lui: Le consultant a dit que c'était beaucoup trop de colonnes pour avoir dans une table.

Moi: Et cela affecte les performances?

Lui: Oui, les utilisateurs ont signalé des ralentissements intermittents dans le module XYZ de l'application.

Moi: Comment savez-vous que la largeur du tableau est la source du problème?

Lui: C'est la table clé utilisée par le module XYZ, et c'est comme 200 colonnes. Ça doit être le problème.

Moi (expliquant): Mais le module XYZ en particulier utilise la plupart des colonnes de cette table, et les colonnes qu'il utilise sont imprévisibles car l'utilisateur configure l'application pour afficher les données qu'il souhaite afficher à partir de cette table. Il est probable que 95% du temps nous finirions par rejoindre toutes les tables de toute façon, ce qui nuirait à la performance.

Lui: Le consultant a dit que c'était trop large et que nous devions le changer.

Moi: Qui est ce consultant? Je ne savais pas que nous avions embauché un consultant, et ils n'avaient pas du tout parlé à l'équipe de développement.

Lui: Eh bien, nous ne les avons pas encore embauchés. Cela fait partie d'une proposition qu'ils ont proposée, mais ils ont insisté sur le fait que nous devions restructurer cette base de données.

Moi: Euh hein. Ainsi, le consultant qui vend des services de refonte de base de données pense que nous avons besoin d'une refonte de base de données ...

La conversation a continué comme ça. Ensuite, j'ai jeté un autre regard sur le tableau en question et j'ai déterminé qu'il pourrait probablement être réduit avec une simple normalisation sans avoir besoin de stratégies de partitionnement exotiques. Ceci, bien sûr, s'est avéré être un point discutable une fois que j'ai enquêté sur les problèmes de performance (auparavant non signalés) et les ai traqués jusqu'à deux facteurs:

  1. Index manquants sur quelques colonnes clés.
  2. Quelques analystes de données malhonnêtes qui verrouillaient périodiquement des tables clés (y compris celle «trop large») en interrogeant la base de données de production directement avec MSAccess.

Bien sûr, l'architecte pousse toujours pour un cloisonnement vertical de la table accroché au méta-problème «trop large». Il a même renforcé sa thèse en obtenant une proposition d'un autre consultant en base de données qui a pu déterminer que nous devions apporter des modifications de conception majeures à la base de données sans regarder l'application ni exécuter d'analyse de performances.


Aaag MSAccess pour prod. Nous avons écrit une procédure pour abandonner toutes les connexions d'accès toutes les quelques minutes pour finalement faire passer le message que c'était mauvais.
Nat

1
Nous avions un travail similaire, mais il était tombé en désuétude. Pour être juste, Access n'est pas le problème, il permet simplement aux néophytes de créer / exécuter des requêtes non performantes.
JohnFx

Nous avons une dépendance dans notre entreprise sur les connexions d'accès ad hoc héritées à la base de données de production. Rien de tel que quelques SQL'ers occasionnels pour oublier une clause WHERE et bloquer les tables principales!
HardCode

35
"J'ai entendu dire que mauve a le plus de RAM"
Piskvor a quitté le bâtiment

Ça aurait pu être pire. L'éditeur de requêtes Excel verrouille la base de données complète lors de son utilisation. Quand je n'étais pas au courant, j'en ai laissé une instance ouverte une grande partie de la journée pendant que je travaillais dans autre chose. Le pire, c'est que MS SQL Server n'a pas signalé le nom d'utilisateur / la machine correcte qui faisait le verrou. J'ai réalisé des heures plus tard que j'étais la raison du verrouillage car les tables étaient verrouillées faisant partie de la vue que j'étais en train d'interroger et d'avoir vérifié tout le reste en premier.
Esteban Küber

58

J'ai vu des gens utiliser alphadrive-7 pour incuber totalement CHX-LT. C'est une pratique rare. La pratique la plus courante consiste à initialiser le transformateur ZT afin que la mise en mémoire tampon soit réduite (en raison d'une plus grande résistance à la surcharge nette) et à créer des bytegraphications de style Java.

Totalement pessimiste!


10
peut-être qu'ils essayaient d'embiggen le condensateur de flux
Mikeage

6
Donc, fondamentalement, le seul nouveau principe impliqué est qu'au lieu de la puissance générée par le mouvement relatif des conducteurs et des flux, elle est produite par l'interaction modale de la magnéto-réluctance et de la duractance capacitive?
Matt Rogish

17
+1 parce que mon moniteur avait de toute façon besoin d'être nettoyé ;-)
RBerteig

1
Zut!! Mais qu'en est-il de l'effet ecletromagentic-cross-genetique. Je pense que cela devrait également être pris en considération. Ou le sujet peut devenir un zombie.
Suraj Chandran

1
Trois mots: «Roulements de silencieux chromés».
Allbite

53

Rien de bouleversant, je l'admets, mais j'ai surpris des gens en train d'utiliser StringBuffer pour concaténer des chaînes en dehors d'une boucle en Java. C'était quelque chose de simple comme tourner

String msg = "Count = " + count + " of " + total + ".";

dans

StringBuffer sb = new StringBuffer("Count = ");
sb.append(count);
sb.append(" of ");
sb.append(total);
sb.append(".");
String msg = sb.toString();

C'était une pratique assez courante d'utiliser la technique en boucle, car elle était sensiblement plus rapide. Le fait est que StringBuffer est synchronisé, donc il y a en fait une surcharge supplémentaire si vous ne concaténez que quelques chaînes. (Sans compter que la différence est absolument insignifiante à cette échelle.) Deux autres points à propos de cette pratique:

  1. StringBuilder n'est pas synchronisé, il doit donc être préféré à StringBuffer dans les cas où votre code ne peut pas être appelé à partir de plusieurs threads.
  2. Les compilateurs Java modernes transformeront la concaténation de chaînes lisible en bytecode optimisé pour vous quand c'est approprié de toute façon.

3
Premièrement: pourquoi n'utiliseriez-vous pas au moins Java 5? Deuxièmement: oui, vous pouvez. Comment pouvez-vous compter jusqu'à 5 dans le premier exemple, mais pas dans le second? Il utilise les mêmes littéraux String que le premier. Écrivez du code lisible et laissez le compilateur décider quand utiliser StringBuffer dans les coulisses.
Bill the Lizard

4
@ MetroidFan2002: Les littéraux de chaîne dans le deuxième exemple sont également des objets. Comme je l'ai dit dans la réponse, les différences sont insignifiantes à cette échelle.
Bill the Lizard

1
Cela ne signifie pas qu'il remplace chaque String par son propre StringBuffer. L'optimisation effectuée par le compilateur réduit le nombre d'objets créés.
Bill the Lizard

3
@Eric: String msg = "Count =" + count + "of" + total + "."; est souvent compilé en Java en String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... c'est précisément ce que fait le deuxième exemple.
Grant Wagner

3
M. Wagner le fait est que VOUS devez regarder tous ces appels de méthode, pas le compilateur. Vous devez les écrire et les comprendre plus tard. Le compilateur fait de même de toute façon. La lisibilité est donc plus importante dans ce cas.
ypnos

47

J'ai vu une fois une base de données MSSQL qui utilisait une table «Root». La table racine avait quatre colonnes: GUID (uniqueidentifier), ID (int), LastModDate (datetime) et CreateDate (datetime). Toutes les tables de la base de données étaient une clé étrangère vers la table racine. Chaque fois qu'une nouvelle ligne était créée dans une table de la base de données, vous deviez utiliser quelques procédures stockées pour insérer une entrée dans la table racine avant de pouvoir accéder à la table réelle qui vous intéressait (plutôt qu'à la base de données faisant le travail pour vous avec quelques déclencheurs simples).

Cela a créé un fouillis de maux de tête et d'écoutes inutiles, a nécessité tout écrit dessus pour utiliser des sprocs (et élimine mes espoirs d'introduire LINQ dans l'entreprise. C'était possible mais cela ne valait tout simplement pas le mal de tête), et pour couronner le tout, ça n'a pas été le cas. t même accomplir ce qu'il était censé faire.

Le développeur qui a choisi ce chemin l'a défendu en supposant que cela économisait des tonnes d'espace parce que nous n'utilisions pas de Guids sur les tables elles-mêmes (mais ... n'est-ce pas un GUID généré dans la table racine pour chaque ligne que nous créons?) , amélioré les performances en quelque sorte et rendu «facile» l'audit des modifications apportées à la base de données.

Oh, et le diagramme de la base de données ressemblait à une araignée mutante de l'enfer.


42

Que diriez-vous de POBI - pessimisation évidemment par intention?

Mon collègue dans les années 90 était fatigué de se faire botter le cul par le PDG simplement parce que le PDG passait le premier jour de chaque version de logiciel ERP (une version personnalisée) à localiser les problèmes de performance dans les nouvelles fonctionnalités. Même si les nouvelles fonctionnalités grignotaient des gigaoctets et rendaient l'impossible possible, il trouvait toujours un détail, voire un problème apparemment majeur, sur lequel se plaindre. Il croyait en savoir beaucoup sur la programmation et a obtenu ses coups de pied en donnant des coups de pied aux programmeurs.

En raison de la nature incompétente de la critique (il était PDG, pas un informaticien), mon collègue n'a jamais réussi à faire les choses correctement. Si vous n'avez pas de problème de performances, vous ne pouvez pas l'éliminer ...

Jusqu'à une version, il a mis beaucoup d'appels de fonction Delay (200) (c'était Delphi) dans le nouveau code. Cela n'a pris que 20 minutes après la mise en service et il a reçu l'ordre de se présenter dans le bureau du PDG pour récupérer en personne ses insultes en retard.

La seule chose inhabituelle à ce jour était que mes collègues étaient muets quand il est revenu, souriant, plaisantant, sortant pour un BigMac ou deux alors qu'il donnait normalement des coups de pied aux tables, s'enflammait au sujet du PDG et de l'entreprise, et passait le reste de la journée à être rejeté à mort. .

Naturellement, mon collègue s'est maintenant reposé pendant un ou deux jours à son bureau, améliorant ses compétences de visée dans Quake - puis le deuxième ou le troisième jour, il a supprimé les appels Delay, reconstruit et publié un "patch d'urgence" dont il a fait passer le mot qu'il avait passé 2 jours et 1 nuit à réparer les trous de performance.

C'était la première (et la seule) fois que le PDG maléfique disait "super boulot!" à lui. C'est tout ce qui compte, non?

C'était du vrai POBI.

Mais c'est aussi une sorte d'optimisation des processus sociaux, donc c'est 100% ok.

Je pense.


10
Je me souviens de quelqu'un qui écrivait sur une application de traitement de données qui était vendue à différents niveaux où le "Lite" ne pouvait écraser que quelques ensembles de données par seconde, la version "superduper" des milliers. La seule différence de code source étant Sleep (N).
peterchen

1
Brillant! Je recommanderais cette norme dans une situation comme celle-là. Au début du développement, allouez une grande partie de la mémoire et ajoutez quelques appels de sommeil, et chaque fois que vous avez besoin de quelques performances, réduisez-les simplement. Ça s'appelle être un faiseur de miracles;)
RCIX

Malheureusement, il est facile de patcher Sleeps en NOP, de sorte que la version allégée peut être craquée très facilement. Cette réserve "d'optimisation" peut nécessiter un packer exécutable pour rendre le débogage et les correctifs plus difficiles.
TheBlastOne

32

"Indépendance de la base de données". Cela signifiait pas de procs, déclencheurs, etc. stockés - pas même de clés étrangères.


8
Est-ce cette «indépendance» dans le sens où vous êtes si loin au-dessus de la base de données, vous avez oublié ce que sont les données? Abstraction inutile des bases de données "pour éviter les problèmes de migration" est une bête noire; tu n'en auras pas besoin.
Rob

8
Plutôt. Astronautes d'architecture au travail. Je construis des applications Web depuis qu'il y avait un Web, et pendant tout ce temps, je n'ai jamais vraiment passé d'une plate-forme de base de données à une autre.
chris

5
Je suis sûr que cela arrive, mais c'est assez rare pour que vous soyez un idiot si vous concevez votre architecture autour de cette possibilité.
chris

6
harpo, c'est une situation différente - c'est une exigence dans ce cas. Je parle de quand ce n'est pas une exigence, mais que les AA décident que cela «pourrait l'être» à un moment donné.
chris

3
@All: l'indépendance de la base de données peut vous coûter cher, oui, mais notre produit est exécuté dans des environnements où le fournisseur de la base de données est choisi par enchères, et nous devons essentiellement jouer le jeu. Certains développeurs n'ont pas le luxe d'une pile logicielle intégrée verticalement et doivent se débrouiller malgré cela.
Chris R

31
var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();

Meilleure utilisation d'un StringBuilder que j'ai jamais vu.


9
Parlez de "pas clair sur le concept"! Hou la la!
Eddie du

3
Cool. "Mon prospect dit que je dois utiliser la classe StringBuilder si je veux concaténer des chaînes. C'est ce que je fais. Alors qu'est-ce qui ne va pas?" Lol ...
TheBlastOne

26

Utiliser une expression régulière pour diviser une chaîne lorsqu'une simple chaîne.


25
MAIS en Java String.Split utilise un regex!
Frank Krueger

Je ne vois pas comment une expression régulière pourrait être aussi rapide qu'une division de chaîne interne.
Andrei Rînea

2
Mais traquer délibérément une expression régulière utilisée pour le fractionnement de cordes et le remplacer par une fonction de partage «simple» sonne comme un exemple parfait de pessimisation. Les bibliothèques Regex sont assez rapides.
David Crawshaw

5
@David Crawshaw: Traquer les opportunités de micro-optimisation fait perdre du temps à l'homme; bud lors de l' écriture de code, utilisez la solution suffisante la moins complexe.
Piskvor a quitté le bâtiment

6
-1: Si vous êtes habitué aux expressions rationnelles, il est très naturel d'écrire ceci au lieu de vous habituer aux 1001 manipulateurs de chaînes internes au langage.
KillianDS

26

Très tard dans ce fil, je sais, mais j'ai vu cela récemment:

bool isFinished = GetIsFinished();

switch (isFinished)
{
    case true:
        DoFinish();
        break;

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

Tu sais, juste au cas où un booléen aurait des valeurs supplémentaires ...


22
True, False an FileNotFound ofcourse
Ikke

Hé, vous devriez toujours avoir une valeur par défaut / case else / etc. Que se passe-t-il lorsqu'une personne brillante change ce booléen en une énumération pour refléter un autre statut, puis la personne suivante ajoute à l'énumération et oublie de modifier la procédure? Avoir une valeur par défaut lorsque vous n'en avez pas besoin ne coûte aucun temps d'exécution et très peu de temps de développement. Traquer une erreur logique accidentellement introduite pendant l'exécution ... Cela coûte du temps, de l'argent et de la réputation. Un point à temps en vaut neuf.
Oorang

1
@Oorang ... pourquoi l'auriez-vous comme interrupteur de toute façon? C'est un booléen - un if / else est tout ce qui est nécessaire.
Damovisa

@Damovisa facepalm à droite ... très bien alors :)
J'ai

2
It was Nullable <Boolean> ... :)
George Chakhidze

25

Le pire exemple auquel je puisse penser est une base de données interne de mon entreprise contenant des informations sur tous les employés. Il reçoit une mise à jour nocturne des ressources humaines et dispose d'un service Web ASP.NET en plus. De nombreuses autres applications utilisent le service Web pour remplir des éléments tels que des champs de recherche / déroulant.

Le pessimisme est que le développeur pensait que des appels répétés au service Web seraient trop lents pour effectuer des requêtes SQL répétées. Alors, qu'est ce qu'il a fait? L'événement de démarrage de l'application lit l'ensemble de la base de données et la convertit en objets en mémoire, stockés indéfiniment jusqu'à ce que le pool d'applications soit recyclé. Ce code était si lent qu'il faudrait 15 minutes pour se charger en moins de 2000 employés. Si vous avez recyclé par inadvertance le pool d'applications pendant la journée, cela peut prendre 30 minutes ou plus, car chaque demande de service Web démarre plusieurs recharges simultanées. Pour cette raison, les nouvelles recrues n'apparaîtraient pas dans la base de données le premier jour de la création de leur compte et ne seraient donc pas en mesure d'accéder à la plupart des applications internes les premiers jours, en se tournant les pouces.

Le deuxième niveau de pessimisme est que le responsable du développement ne veut pas y toucher de peur de casser des applications dépendantes, mais nous continuons à avoir des pannes sporadiques d'applications critiques à l'échelle de l'entreprise en raison de la mauvaise conception d'un composant aussi simple.


28
La gestion à son meilleur - "Non, ne consacrons pas 80 heures de programmation ponctuelles à la réparation de cette application, c'est trop cher. Gardons-la simplement pour que ses bogues puissent épuiser plus de 200 heures utilisateur par mois, plus 10 heures de programmeur par mois pour 'entretien'." AAAAAAAAAUGH !!!
Piskvor a quitté le bâtiment

25

Personne ne semble avoir mentionné le tri, alors je le ferai.

Plusieurs fois, j'ai découvert que quelqu'un avait fabriqué à la main un tri à bulles, parce que la situation «ne nécessitait pas» un appel à l'algorithme de tri rapide «trop sophistiqué» qui existait déjà. Le développeur a été satisfait de constater que leur trousse à bulles artisanale fonctionnait assez bien sur les dix lignes de données qu'ils utilisent pour les tests. Cela ne s'est pas passé aussi bien après que le client ait ajouté quelques milliers de lignes.


2
Je l'ai fait moi-même une fois, lorsque j'ai déterminé que généralement n = 2. Les améliorations ultérieures du produit ont invalidé ma prémisse, et le code a été remplacé PDQ.
Mark Ransom le

2
Ouais, mais c'est bien d'écrire quelque chose d' algorythme de temps en temps;)
UpTheCreek

20

J'ai déjà travaillé sur une application pleine de code comme celui-ci:

 1 tuple *FindTuple( DataSet *set, int target ) {
 2     tuple *found = null;
 3     tuple *curr = GetFirstTupleOfSet(set);
 4     while (curr) {
 5         if (curr->id == target)
 6             found = curr;
 7         curr = GetNextTuple(curr);
 8     }
 9     return found;
10 }

Supprimer simplement found, revenir nullà la fin et changer la sixième ligne en:

            return curr;

Les performances de l'application ont doublé.


1
J'ai travaillé une fois dans une entreprise où les directives de codage exigeaient "un seul retour à la fin" (pour la maintenance). Et en effet, certains crachent du code comme le vôtre, parce qu'ils ne pensent pas (les solutions évidentes consistaient la plupart du temps à utiliser un goto à la sortie proc, ou à modifier la condition de sortie des boucles)
flolo

12
Une monnaie de retour produit ici un comportement sensiblement différent. Lorsque vous revenez curr, vous obtenez la PREMIÈRE correspondance, alors que le code que vous avez collé renvoie la DERNIÈRE correspondance.
SoapBox

2
@SoapBox: Vous avez raison. @Dour High Arch: L'augmentation des performances n'a rien à voir avec la règle de retour unique, car Flolo a dit que le fait de faire passer la condition de boucle à (curr &&! Found) aurait le même effet. GOTO à la sortie de proc est horrible et va à l'encontre du but de la directive de retour unique.
Akusete

2
Bons commentaires tout le monde. Dans ce cas, il ne devait y avoir qu'un seul tuple avec chaque ID.
Dour High Arch

7
Ce n'est pas une «pessimisation», n'est-ce pas? Il s'agit simplement d'une optimisation en attente.
Tim Long

20

Une fois, j'ai dû essayer de modifier le code qui incluait ces gemmes dans la classe Constants

public static String COMMA_DELIMINATOR=",";
public static String COMMA_SPACE_DELIMINATOR=", ";
public static String COLIN_DELIMINATOR=":";

Chacun de ces éléments a été utilisé plusieurs fois dans le reste de l'application à des fins différentes. COMMA_DELIMINATOR a jonché le code avec plus de 200 utilisations dans 8 packages différents.


Au moins quelque chose comme ça est facile à trouver / remplacer hors de la source - toujours mes sympathies.
Erik Forbes

12
Aussi - Deliminator? Je pensais que c'était épelé «délimiteur». Deliminator ressemble à un mauvais film du milieu des années 90 qui a en quelque sorte 3 suites ...........
Erik Forbes

53
Deliminator III: Rise of the Commas
Rob

33
Sur une autre note, je suis heureux de voir une délimitation appropriée de Colins. Tout programmeur digne de ce nom sait que s'il y a une chose que vous devez absolument séparer correctement, ce sont les fichus Colins.
Rob

2
Ce n'est pas si facile de faire une recherche et un remplacement appropriés. Puisque chacun est utilisé à des fins différentes. Tout bon programmeur aurait au moins fait quelque chose comme ceci: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... etc
KitsuneYMG

19

Le grand numéro un de tous les temps que je rencontre maintes et maintes fois dans le logiciel interne:

Ne pas utiliser les fonctionnalités du SGBD pour des raisons de "portabilité" car "nous pourrions vouloir passer à un autre fournisseur plus tard".

Lis sur mes lèvres. Pour tout travail en interne: IL N'ARRIVERA PAS!


9
Cela arrive. MySQL -> postgresql, donc nous n'avons rien perdu .
Thomas

Ou postgres / postgis -> sqlite / spatialite ... C'était une douleur dans le cul ...
Philip

cela se produit dans les tests JUnit
kachanov

17

J'avais un collègue qui essayait de déjouer l'optimiseur de notre compilateur C et le code de réécriture de routine que lui seul pouvait lire. L'une de ses astuces préférées était de changer une méthode lisible comme (créer du code):

int some_method(int input1, int input2) {
    int x;
    if (input1 == -1) {
        return 0;
    }
    if (input1 == input2) {
        return input1;
    }
    ... a long expression here ...
    return x;
}

dans ceci:

int some_method() {
    return (input == -1) ? 0 : (input1 == input2) ? input 1 :
           ... a long expression ...
           ... a long expression ...
           ... a long expression ...
}

Autrement dit, la première ligne d'une méthode une fois lisible deviendrait " return" et toute autre logique serait remplacée par des expressions terniaires profondément imbriquées. Lorsque vous avez essayé de discuter du fait que cela était impossible à maintenir, il soulignait le fait que la sortie d'assemblage de sa méthode était plus courte de trois ou quatre instructions d'assemblage. Il n'a pas été nécessairement plus rapide , mais il a toujours été un petit peu plus court. Il s'agissait d'un système embarqué où l'utilisation de la mémoire importait parfois, mais il y avait des optimisations beaucoup plus faciles qui auraient pu être faites que celles-ci qui auraient laissé le code lisible.

Puis, après cela, pour une raison quelconque, il a décidé que ptr->structElementc'était trop illisible, alors il a commencé à changer tout cela en (*ptr).structElementsur la théorie que c'était plus lisible et plus rapide aussi.

Transformer un code lisible en code illisible pour au plus une amélioration de 1%, et parfois même un code plus lent.


Si ledit module est appelé des millions et des millions de fois par boucle, alors j'approuverais cette optimisation tant qu'il en commenterait le diable.
Michael Dorgan

2
@Michael: Je ne le ferais pas, à moins qu'il y ait des mesures indiquant que c'était plus rapide , pas seulement plus court .
dsimcha

Dans la plupart des situations, l'opérateur ternaire est plus lisible que if. L'insistance sur les déclarations sur les expressions en C est un dogme culturel / religieux, pas une quelconque pratique objective. (Meilleure recommandation: si le ternaire imbriqué est trop long à lire, vous ne devriez pas l'utiliser ifnon plus.)
Leushenko

2
Le problème ici est de prendre une fonction entière et de la remplacer par une seule instruction, un retour, remplaçant ainsi toute la logique de la fonction entière par des ternaires imbriqués. Si vous le voyiez, vous comprendriez. Ce n'est pas une chose religieuse "Je déteste les opérateurs ternaires". Je ne parle pas de prendre un seul ifdans une fonction et de le remplacer par un ternaire. C'est bien, et souvent plus lisible. Je parle de remplacer une méthode entière de plus de 30 lignes par une seule instruction de retour et des ternaires imbriqués. Personne ne pensait que le nouveau code était plus lisible, mais un développeur pensait qu'il était plus rapide.
Eddie

15

Dans l'un de mes premiers emplois en tant que développeur à part entière, j'ai repris un projet pour un programme qui souffrait de problèmes de mise à l'échelle. Cela fonctionnerait raisonnablement bien sur de petits ensembles de données, mais tomberait complètement en panne lorsque de grandes quantités de données étaient fournies.

En creusant, j'ai trouvé que le programmeur d'origine cherchait à accélérer les choses en parallélisant l'analyse - en lançant un nouveau thread pour chaque source de données supplémentaire. Cependant, il avait commis une erreur en ce que tous les threads nécessitaient une ressource partagée, sur laquelle ils se bloquaient. Bien sûr, tous les avantages de la concurrence ont disparu. De plus, il a planté la plupart des systèmes pour lancer plus de 100 threads pour les verrouiller tous sauf un. Ma machine de développement robuste était une exception en ce sens qu'elle parcourait un ensemble de données de 150 sources en environ 6 heures.

Donc, pour résoudre ce problème, j'ai supprimé les composants multi-threading et nettoyé les E / S. En l'absence d'autres changements, le temps d'exécution sur l'ensemble de données de 150 sources est passé en dessous de 10 minutes sur ma machine et de l'infini à moins d'une demi-heure sur la machine moyenne de l'entreprise.


J'empêche juste que cela se produise dans un projet aujourd'hui. Maintenant je sais que j'ai fait le bon choix.
deadalnix

14

Je suppose que je pourrais offrir ce bijou:

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1, root = 0;
    #define ISQRT_INNER(shift) \
    { \
        if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
        { \
            root += 1 << shift; \
            value -= tmp; \
        } \
    }

    // Find out how many bytes our value uses
    // so we don't do any uneeded work.
    if (value & 0xffff0000)
    {
        if ((value & 0xff000000) == 0)
            tmp = 3;
        else
            tmp = 4;
    }
    else if (value & 0x0000ff00)
        tmp = 2;

    switch (tmp)
    {
        case 4:
            ISQRT_INNER(15);
            ISQRT_INNER(14);
            ISQRT_INNER(13);
            ISQRT_INNER(12);
        case 3:
            ISQRT_INNER(11);
            ISQRT_INNER(10);
            ISQRT_INNER( 9);
            ISQRT_INNER( 8);
        case 2:
            ISQRT_INNER( 7);
            ISQRT_INNER( 6);
            ISQRT_INNER( 5);
            ISQRT_INNER( 4);
        case 1:
            ISQRT_INNER( 3);
            ISQRT_INNER( 2);
            ISQRT_INNER( 1);
            ISQRT_INNER( 0);
    }
#undef ISQRT_INNER
    return root;
}

Puisque la racine carrée a été calculée à un endroit très sensible, j'ai eu la tâche de chercher un moyen de la rendre plus rapide. Ce petit refactoring a réduit le temps d'exécution d'un tiers (pour la combinaison du matériel et du compilateur utilisé, YMMV):

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1, root = 0;
    #define ISQRT_INNER(shift) \
    { \
        if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
        { \
            root += 1 << shift; \
            value -= tmp; \
        } \
    }

    ISQRT_INNER (15);
    ISQRT_INNER (14);
    ISQRT_INNER (13);
    ISQRT_INNER (12);
    ISQRT_INNER (11);
    ISQRT_INNER (10);
    ISQRT_INNER ( 9);
    ISQRT_INNER ( 8);
    ISQRT_INNER ( 7);
    ISQRT_INNER ( 6);
    ISQRT_INNER ( 5);
    ISQRT_INNER ( 4);
    ISQRT_INNER ( 3);
    ISQRT_INNER ( 2);
    ISQRT_INNER ( 1);
    ISQRT_INNER ( 0);

#undef ISQRT_INNER
    return root;
}

Bien sûr, il existe à la fois des moyens plus rapides ET meilleurs pour le faire, mais je pense que c'est un bel exemple de pessimisation.

Edit: À bien y penser, la boucle déroulée était en fait aussi une pessimisation soignée. En creusant le contrôle de version, je peux également présenter la deuxième étape de la refactorisation, qui a été encore meilleure que celle ci-dessus:

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1 << 30, root = 0;

    while (tmp != 0)
    {
        if (value >= root + tmp) {
            value -= root + tmp;
            root += tmp << 1;
        }
        root >>= 1;
        tmp >>= 2;
    }

    return root;
}

C'est exactement le même algorithme, bien qu'une implémentation légèrement différente, donc je suppose qu'il se qualifie.


Je suppose qu'il isqrt()calcule floor(sqrt()), mais pourquoi ce code fonctionne-t-il?
Pablo H

11

Cela peut être à un niveau plus élevé que ce que vous recherchiez, mais le réparer (si vous y êtes autorisé) implique également un niveau de douleur plus élevé:

Insister sur le roulement d'un gestionnaire de relations d'objet / couche d'accès aux données au lieu d'utiliser l'une des bibliothèques établies, testées et matures (même après vous avoir été signalées).


Ce n'est pas toujours une mauvaise idée de rouler votre propre code. Comme un sage l'a dit un jour, trouvez les dépendances et éliminez-les. S'il s'agit d'une fonction commerciale essentielle, faites-le vous-même.
Kibbee

Je n'ai jamais déduit que c'était toujours une mauvaise idée. Sauf si vous dites Frans Bouma ou similaire, je doute que les éléments ORM / DAL soient une fonction commerciale essentielle. Il est extrêmement coûteux d'écrire votre propre équivalent, un cas de réinventer la roue (carrée), généralement en raison du syndrome des NIH.
Gordon Hartley

@Kibbee - Je suis d'accord. Il vaut mieux rouler le vôtre et le comprendre que d'utiliser des dépendances tierces. Quand ça casse (et ça va) au moins vous pouvez le réparer alors. J'ai trouvé des bogues dans Hibernate et Apache Commons dans le passé qui tuaient absolument les performances de notre application.
CodingWithSpike

4
Le rouler à la main est vraiment votre seule option si aucune des fonctionnalités existantes n'a une fonctionnalité essentielle dont vous avez besoin.
staticsan

3
En fait, compte tenu de certains des commentaires ci-dessus, un peu plus de perspective: une autre pessimisation est d'essayer de faire en sorte que l'ORM fasse absolument tout. C'est souvent utile pour 95% + des cas. Pour ces derniers 5%, il est beaucoup plus facile d'abandonner le code de persistance fabriqué à la main / les appels de procédure stockée directe, etc. pour des raisons de performance, de simplicité ou les deux.
Gordon Hartley

10

Toutes les contraintes de clé étrangère ont été supprimées d'une base de données, car sinon, il y aurait tellement d'erreurs.


8

Cela ne correspond pas exactement à la question, mais je le mentionnerai quand même un récit édifiant. Je travaillais sur une application distribuée qui fonctionnait lentement et je me suis envolé pour DC pour participer à une réunion visant principalement à résoudre le problème. Le chef de projet a commencé à esquisser une ré-architecture visant à résoudre le retard. J'ai déclaré que j'avais pris des mesures au cours du week-end qui isolaient le goulot d'étranglement à une seule méthode. Il s'est avéré qu'il y avait un enregistrement manquant lors d'une recherche locale, obligeant l'application à se rendre sur un serveur distant à chaque transaction. En ajoutant l'enregistrement au magasin local, le retard a été éliminé - le problème a été résolu. Notez que la ré-architecture n'aurait pas résolu le problème.


8

Vérifier avant TOUTES les opérations javascript si l'objet sur lequel vous travaillez existe.

if (myObj) { //or its evil cousin, if (myObj != null) {
    label.text = myObj.value; 
    // we know label exists because it has already been 
    // checked in a big if block somewhere at the top
}

Mon problème avec ce type de code est que personne ne semble se soucier que se passe-t-il s'il n'existe pas? Ne rien faire? Ne donnez pas la rétroaction à l'utilisateur?

Je conviens que les Object expectederreurs sont ennuyeuses, mais ce n'est pas la meilleure solution pour cela.


Quelle est alors la meilleure solution? Je pense qu'il est bâclé d'écrire du code, où des erreurs se produisent occasionnellement, même si elles n'ont pas de conséquences directes. Bien sûr, vous ne devriez pas le faire, si vous ne vous attendez pas à ce que l'objet soit nul en aucune circonstance - c'est peut-être ce que vous vouliez dire.
simon

7

Et l'extrémisme YAGNI. C'est une forme de pessimisation prématurée. Il semble que chaque fois que vous appliquez YAGNI, vous finissez par en avoir besoin, ce qui entraîne 10 fois plus d'efforts pour l'ajouter que si vous l'aviez ajouté au début. Si vous créez un programme réussi, il y a de fortes chances que vous en ayez besoin. Si vous avez l'habitude de créer des programmes dont la durée de vie s'épuise rapidement, continuez à pratiquer YAGNI car alors je suppose YAGNI.


3
Merci, j'en ai assez de ces acronymes boiteux de «programmation extrême» et de la façon dont les gens les utilisent pour soutenir des pratiques paresseuses et contre-productives.
JAL

Des études de projets réels montrent que le facteur réel entre le code à usage unique et le code réutilisable est en moyenne d'environ 3. Donc, 10 est juste la valeur «ressentie», mais vous avez raison par intention.
peterchen

@peterchen - êtes-vous en train de dire que des études montrent qu'il faut trois fois plus de temps pour écrire du code réutilisable en tant que code unique, ou qu'elles montrent qu'il faut trois fois plus de temps pour convertir un code unique en code réutilisable que pour écrire le code réutilisable en premier lieu?
Jeff Sternal

@jeff: IIRC a comparé une mesure de complexité (quoi que vous en pensiez) d'extraits en ligne qui ont été déplacés vers des méthodes distinctes. La complexité augmente en raison de cas supplémentaires pris en charge, de la vérification des paramètres, etc. (ce qui me fait supposer que les méthodes étaient plutôt petites). Laissez-moi essayer de trouver une référence.
peterchen

6

Pas exactement une optimisation prématurée - mais certainement malavisée - cela a été lu sur le site Web de la BBC, à partir d'un article sur Windows 7.

M. Curran a déclaré que l'équipe Microsoft Windows s'était penchée sur tous les aspects du système d'exploitation pour apporter des améliorations. «Nous avons pu réduire de 400 millisecondes le temps d’arrêt en coupant légèrement la musique d’arrêt du fichier WAV.

Maintenant, je n'ai pas encore essayé Windows 7, donc je me trompe peut-être, mais je suis prêt à parier qu'il y a d'autres problèmes qui sont plus importants que le temps qu'il faut pour s'arrêter. Après tout, une fois que je vois le message «Arrêter Windows», le moniteur s'éteint et je m'éloigne - en quoi ces 400 millisecondes me profitent-elles?


Vous constaterez probablement que les autres problèmes ne sont pas aussi faciles à expliquer aux non-programmeurs sur un site Web de la BBC.
Tom Leys

Maintenant c'est un angle que je n'ai pas considéré - peut-être que je commence à perdre mon cynisme :-)
belugabob

Ces 400 ms correspondent à 400 ms de consommation électrique. Probablement insignifiant, mais peut-être que cela s'additionne avec le temps. Pourtant, je ne m'inquiéterais pas.
ZachS

1
J'ai perdu beaucoup d'heures au total à attendre l'arrêt des VM XP pour pouvoir passer à la chose suivante. Je suis très reconnaissant pour un arrêt plus rapide.
James

1
Fait intéressant, les fichiers WAV sont lus de manière asynchrone, donc tant que la fanfare d'arrêt est plus courte que le temps nécessaire pour s'arrêter, le découpage du fichier WAV ne fait rien. Et encore plus intéressant, s'ils ont tellement optimisé l'arrêt, comment se fait-il que chaque boîtier Windows que j'arrête ait besoin de éternités jusqu'à ce qu'il soit vraiment éteint? (Sauf pour l'utilisation du gros bouton rouge, bien sûr.)
TheBlastOne

6

Quelqu'un dans mon département a déjà écrit une classe de chaînes. Une interface comme CString, mais sans la dépendance Windows.

Une "optimisation" qu'ils ont faite était de ne pas allouer plus de mémoire que nécessaire. Apparemment, ne pas se rendre compte que la raison pour laquelle les classes comme std::stringallouent un excès de mémoire est qu'une séquence d' +=opérations peut s'exécuter en un temps O (n).

Au lieu de cela, chaque +=appel a forcé une réallocation, qui a transformé des appendices répétés en un algorithme de Schlemiel le peintre O (n²) .


5

Un de mes anciens collègues (un soab , en fait) a été chargé de créer un nouveau module pour notre ERP Java qui aurait dû collecter et analyser les données des clients (commerce de détail). Il a décidé de diviser CHAQUE champ Calendrier / Date / Heure en ses composants (secondes, minutes, heures, jour, mois, année, jour de la semaine, bimestre, trimestre (!)) Parce que "comment pourrais-je demander" tous les lundis "?"


3
Ce n'est pas une optimisation prématurée, il pensait qu'il devait le faire pour l'exactitude
Pyrolistique

Bien sûr, il pensait qu'il en avait besoin, mais comme la plupart des SGBD ont une sorte de fonction DAYOFWEEK (horodatage), faire ce gâchis à l'avance est assez prématuré à mon avis :)
Joril

1
Je ne l'utiliserais pas pour OLTP, mais si vous "analysiez les données des clients", alors c'est en fait une manière très flexible de concevoir un entrepôt de données (tant que la date et l'heure sont divisées en différentes dimensions). Souhaitez-vous vraiment appeler DAYOFWEEK () sur des millions de lignes de données ou simplement faire une recherche d'index sur un champ entier?
Tim Medora

Eh bien, je ne sais pas s'il y avait autant de lignes, mais ce n'est sûrement pas l'explication qui a été donnée :)
Joril

3

Aucune offense pour personne, mais j'ai juste noté un devoir (java) qui avait ceci

import java.lang.*;

1
À moins que ce ne soit une classe de niveau supérieur, je pense que vous devez laisser un peu de temps à cette élève à moins que vous ne lui appreniez suffisamment pour savoir pourquoi ce n'est pas une bonne idée.
Bryan Oakley

24
Vais-je être le seul à noter l'ironie d'un enseignant appelant WTF sur le code d'un élève qu'il / elle est chargé d'enseigner à programmer correctement?
JohnFx

3
Ouais, je ne vois pas que ça ferait mal. Au pire, c'est superflu. Les étudiants ont tendance à recourir à une cohérence rigide pendant qu'ils apprennent, et l'importation de java.lang est strictement cohérente avec ce que l'élève a appris sur l'importation.
cygil

1
Merci à tous de m'avoir dit l'évidence. C'était une mission de biologie computationnelle et je ne l'ai pas comptée, ni même mentionnée.
survolée

2
@JohnFX: Le niveleur et l'enseignant ne sont pas toujours la même personne.
Eddie le
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.