web-dev-qa-db-fra.com

Quelle est la pessimisation la plus ridicule que vous ayez vue?

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 implémente une "optimisation" parce qu'il pensez ce sera plus rapide, mais cela finit par être plus lent, ainsi que bogué, non maintenable, etc. Quel est l'exemple le plus ridicule de cela que vous avez vu?

146
dsimcha

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

Notre nouvel environnement était Sparc Solaris 32 bits.

L'un des gars est allé et a changé tous les entiers en short pour accélérer notre code, car récupérer 16 bits de RAM était plus rapide que de saisir 32 bits.

J'ai dû écrire un programme de démonstration pour montrer que la capture de valeurs 32 bits sur un système 32 bits était plus rapide que la capture de valeurs 16 bits, et expliquer que pour saisir une valeur 16 bits, le CPU devait faire une largeur 32 bits accéder à la mémoire, puis masquer ou décaler les bits non nécessaires pour la valeur 16 bits.

81
Mark Harrison

Je pense que l'expression "l'optimisation prématurée est la racine de tout mal" est très utilisée. Pour de nombreux projets, c'est devenu une excuse pour ne prendre en compte les performances que tard dans un projet.

Cette phrase est souvent une béquille pour que les gens évitent le travail. Je vois cette expression utilisée quand les gens devraient vraiment dire "Gee, nous n'y avons vraiment pas pensé à l'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"

  • Lecture de la même clé de registre des milliers (ou des dizaines de milliers) de fois pendant le lancement du programme.
  • Chargement du même DLL centaines ou milliers de fois
  • Gaspillage de méga octets de mémoire en gardant inutilement les chemins d'accès complets aux fichiers
  • Ne pas organiser les structures de données afin qu'elles occupent beaucoup plus de mémoire que nécessaire
  • Dimensionnement de toutes les chaînes qui stockent les noms de fichiers ou les chemins d'accès à MAX_PATH
  • Interrogation gratuite pour une chose qui a des événements, des rappels ou d'autres mécanismes de notification

Ce que je pense être une meilleure déclaration est la suivante: "l'optimisation sans mesurer et comprendre n'est pas du tout l'optimisation - c'est juste un changement aléatoire".

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

206
Foredecker

Les bases de données sont des terrains de jeux de pessimisation.

Les favoris incluent:

  • Divisez un tableau en multiples (par plage de dates, par ordre alphabétique, etc.) car il est "trop ​​grand".
  • Créez une table d'archivage pour les enregistrements retirés, mais continuez à 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.

C'est du haut de ma tête.

114
dkretz

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

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éballer toutes les données avant de pouvoir les utiliser. Mais je pense que ce n'est pas de la pessimisation, c'est de l'optimisation intelligente pour réduire les coûts de communication.

D'autre part, nos architectes logiciels ont décidé que les données décompressées devaient être formatées dans un document XML très lisible et stockées dans notre base de données en tant que telles (par opposition à avoir chaque champ stocké dans une colonne correspondante). Leur idée était que "XML est le futur", "l'espace disque est bon marché" et "le processeur est bon marché", il n'était donc pas nécessaire 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 pour des requêtes même 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 tous les maux.

87
MiniQuark

Oh mon Dieu, je pense que je les ai tous vus. Plus souvent qu'autrement, c'est un effort pour résoudre les problèmes de performances par quelqu'un qui est sacrément paresseux pour résoudre leur problème jusqu'à la CAUSE de ces problèmes de performances ou même rechercher s'il existe réellement IS 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 qui cherche désespérément un clou qui correspond à son nouveau marteau brillant.

Voici un exemple récent:

L'architecte de données vient à moi avec 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: Le tableau X est trop large, nous le partitionnons pour des raisons de performances.

Moi: Qu'est-ce qui vous fait penser qu'il est trop large?

Lui: Le consultant a dit que c'était 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 de la table est la source du problème?

Lui: C'est le tableau des clés utilisé par le module XYZ, et c'est comme 200 colonnes. Ce doit être le problème.

Moi (Explication): 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 réunir toutes les tables ensemble, ce qui nuirait aux performances.

Lui: Le consultant a dit qu'il est trop large et que nous devons le changer.

Moi: Qui est ce consultant? Je ne savais pas que nous avions engagé un consultant, et ils n'ont 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 ré-architecturer cette base de données.

Moi: Uh huh. 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 s'est poursuivie indéfiniment comme ça. Par la suite, j'ai jeté un nouveau coup d'œil au 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. Bien sûr, cela s'est avéré être un point discutable une fois que j'ai enquêté sur les problèmes de performances (auparavant non signalés) et les ai décelés à deux facteurs:

  1. Index manquants sur quelques colonnes clés.
  2. Quelques analystes de données voyous qui verrouillaient périodiquement les tables de clés (y compris celle "trop ​​large") en interrogeant la base de données de production directement avec MSAccess.

Bien sûr, l'architecte continue de pousser pour une partition verticale de la table accrochée au méta-problème "trop ​​large". Il a même renforcé son dossier en obtenant une proposition d'un autre consultant en base de données qui a pu déterminer que nous avions besoin de modifications majeures de la conception de la base de données sans regarder l'application ou exécuter une analyse des performances.

75
JohnFx

J'ai vu des gens utiliser alphadrive-7 pour incuber totalement CHX-LT. C'est une pratique peu courante. La pratique la plus courante consiste à initialiser le transformateur ZT de manière à réduire la mise en mémoire tampon (en raison d'une plus grande résistance aux surcharges nettes) et à créer Java style bytegraphications).

Totalement pessimiste!

58
Zalaga

Rien de bouleversant, je l'avoue, mais j'ai surpris des gens utilisant 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();

Avant, c'était une pratique assez courante d'utiliser la technique en boucle, car c'était beaucoup plus rapide. Le truc, c'est que StringBuffer est synchronisé, donc il y a en fait des frais supplémentaires si vous ne concaténez que quelques chaînes. (Sans oublier que la différence est absolument négligeable à cette échelle.) Deux autres points à propos de cette pratique:

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

J'ai vu une fois une base de données MSSQL qui utilisait une table 'Root'. La table racine avait quatre colonnes: GUID (identifiant unique), ID (int), LastModDate (datetime) et CreateDate (datetime). Toutes les tables de la base de données étaient à clé étrangère à la racine Chaque fois qu'une nouvelle ligne a été créée dans la table any dans la base de données, vous avez dû 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 dont vous vous souciez. (plutôt que la base de données faisant le travail pour vous avec quelques déclencheurs simples déclencheurs).

Cela a créé un gâchis de maux de tête et de maux de tête inutiles, a exigé que tout ce qui est écrit dessus utilise des sprocs (et élimine mes espoirs d'introduire LINQ dans l'entreprise. 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 les guides sur les tables elles-mêmes (mais ... n'est pas un GUID généré dans le Table racine pour chaque ligne que nous créons?), A amélioré les performances d'une manière ou d'une autre et a 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.

47
Dusda

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

Un de mes collègues dans les années 90 était fatigué de se faire botter le cul par le PDG juste parce que le PDG passait le premier jour de chaque ERP version logicielle (une version personnalisée) avec localisation des problèmes de performances dans le nouvelles fonctionnalités. Même si les nouvelles fonctionnalités crunch gigaoctets et rendu l'impossible possible, il a toujours trouvé quelques détails, ou même un problème apparemment majeur, à pleurnicher. Il croyait en savoir beaucoup sur la programmation et a obtenu ses coups de pied en donnant un coup de pied aux ânes du programmeur.

En raison de la nature incompétente de la critique (il était PDG, pas informaticien), mon collègue n'a jamais réussi à bien faire les choses. 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 ligne, et il a été sommé de comparaître dans le bureau du PDG pour aller chercher ses insultes en retard.

La seule chose inhabituelle jusqu'à présent était que mes collègues soient muets quand il est revenu, souriant, plaisantant, sortant pour un BigMac ou deux alors qu'il normalement donnait un coup de pied aux tables, flambait sur le PDG et la société, et passait le reste de la journée à la 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 de retard, reconstruit et publié un "patch d'urgence" dont il a fait passer le mot. qu'il avait passé 2 jours et 1 nuit pour réparer les trous de performance.

C'était la première (et la seule) fois que le PDG maléfique disait "excellent travail!" à 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.

42
TheBlastOne

"Indépendance de la base de données". Cela signifiait aucun proc stocké, déclencheurs, etc. - pas même aucune clé étrangère.

32
chris
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 vue.

31
luft

Utilisation d'une expression régulière pour fractionner une chaîne lorsqu'une simple chaîne.split suffit

26
Cherian

Très tard pour ce fil, je sais, mais je l'ai vu récemment:

bool isFinished = GetIsFinished();

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

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

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

26
Damovisa

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

Le pessimisme est que le développeur pensait que les 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 d'application lit dans la base de données entière et convertit le tout 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 charger moins de 2000 employés. Si vous avez recyclé par inadvertance le pool d'applications pendant la journée, cela pourrait prendre 30 minutes ou plus, car chaque demande de service Web démarrerait plusieurs rechargements simultanés. 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 pourraient donc pas accéder à la plupart des applications internes au cours de leurs deux premiers jours, se tortillant les pouces.

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

25
spoulson

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 de bulles, car 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 lorsque leur bulle artisanale a fonctionné assez bien sur les dix lignes de données qu'ils utilisent pour les tests. Cela ne s'est pas aussi bien passé après que le client ait ajouté quelques milliers de lignes.

25
Dan Breslau

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 d'eux 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.

20
KitsuneYMG

Le grand numéro un de tous les temps que je rencontre maintes et maintes fois dans les logiciels internes:

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 ne se passera pas!

19
Peter Stuer

J'ai déjà travaillé sur une application pleine de code comme celle-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 }

Il suffit de supprimer found, de retourner null à la fin et de changer la sixième ligne en:

            return curr;

Double les performances de l'application.

19
Dour High Arch

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 consistait à changer une méthode lisible comme (inventer 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;
}

en cela:

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 de la façon dont cela était impossible à maintenir, il a souligné le fait que le résultat de l'Assemblée de sa méthode était trois ou quatre instructions de l'Assemblée plus courtes. Ce n'était pas nécessairement plus rapide mais c'était toujours minuscule un peu plus court. Il s'agissait d'un système embarqué où l'utilisation de la mémoire était parfois importante, mais il y avait des optimisations beaucoup plus faciles qui auraient pu être faites que cela aurait laissé le code lisible.

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

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

17
Eddie

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. Il fonctionnerait raisonnablement bien sur de petits ensembles de données, mais se bloquerait complètement lorsqu'il recevrait de grandes quantités de données.

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 fait une erreur en ce que tous les threads nécessitaient une ressource partagée, sur laquelle ils étaient bloqués. Bien sûr, tous les avantages de la simultanéité ont disparu. De plus, la plupart des systèmes se sont écrasés pour lancer plus de 100 threads uniquement pour que tous, sauf un, se verrouillent. Ma machine de développement costaud était une exception dans la mesure où elle tournait à travers un ensemble de données de 150 sources en environ 6 heures.

Donc, pour y remédier, j'ai supprimé les composants multithread et nettoyé les E/S. En l'absence d'autres modifications, 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.

15
Jeffrey Blake

Je suppose que je pourrais offrir ce joyau:

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;
}

Comme 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és, 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 à travers le contrôle de version, je peux également présenter la deuxième étape de la refactorisation, qui a été encore meilleure que la précédente:

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 est admissible.

14
Christoffer

Cela pourrait être à un niveau supérieur à ce que vous recherchiez, mais le corriger (si vous êtes autorisé) implique également un niveau de douleur plus élevé:

Insister sur le fait de faire rouler un Object Relationship Manager/Data Access Layer au lieu d'utiliser l'une des bibliothèques établies, testées et matures (même après qu'elles vous ont été signalées).

11
Gordon Hartley

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

10
Guge

Cela ne correspond pas exactement à la question, mais je vais quand même le mentionner comme un récit édifiant. Je travaillais sur une application distribuée qui fonctionnait lentement et s'est envolée vers 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. Je me suis porté volontaire pour avoir pris quelques mesures au cours du week-end qui ont isolé le goulot d'étranglement à une seule méthode. Il s'est avéré qu'il y avait un enregistrement manquant sur une recherche locale, obligeant l'application à aller sur un serveur distant chaque transaction. En ajoutant le dossier au magasin local, le retard a été éliminé - problème résolu. Notez que la ré-architecture n'aurait pas résolu le problème.

8
Jack BeNimble

Vérifier avant CHAQUE opération javascript si l'objet sur lequel vous opérez 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 s'il n'existe pas? Ne fais rien? Ne donnez pas le feedback à l'utilisateur?

J'accepte que le Object expected les erreurs sont ennuyeuses, mais ce n'est pas la meilleure solution pour cela.

8
Chetan Sastry

Que diriez-vous de 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, les chances sont que VOUS ALLEZ EN BESOIN. Si vous avez l'habitude de créer des programmes dont la vie s'épuise rapidement, continuez à pratiquer YAGNI car je suppose que YAGNI.

7
Dunk

Pas exactement une optimisation prématurée - mais certainement erroné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 de Microsoft Windows avait examiné 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 réduisant légèrement la musique d'arrêt du fichier WAV.

Maintenant, je n'ai pas encore essayé Windows 7, donc je peux me tromper, 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 arrêter. Après tout, une fois que je vois le message "Arrêter Windows", le moniteur est éteint et je m'en vais - en quoi ces 400 millisecondes me sont-elles profitables?

6
belugabob

Quelqu'un dans mon département a écrit une fois une classe de cordes. Une interface comme CString, mais sans la dépendance Windows.

Une "optimisation" qu'ils ont faite a consisté à pas allouer plus de mémoire que nécessaire. Apparemment, ne réalisant pas que les classes de raison comme std::string ne pas allouer de mémoire excédentaire est telle qu'une séquence de += les opérations peuvent s'exécuter en O(n) fois.

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

6
dan04

Un de mes anciens collègues (un soab , en fait) a été affecté à la construction d'un nouveau module pour notre Java ERP qui devrait ont collecté et analysé les données des clients (commerce de détail). Il a décidé de diviser CHAQUE champ Calendrier/Datetime en ses composants (secondes, minutes, heures, jour, mois, année, jour de la semaine, bimester, trimestre (!)) parce que " sinon, comment pourrais-je demander "tous les lundis"? "

5
Joril

Je ne pense pas que la pessimisation soit rare. D'après mon expérience dans l'optimisation des performances, une grande partie des mauvaises performances est causée par de "bonnes pratiques de programmation" justifiées au nom de "l'efficacité". Exemples:

  • Collections de cartes ou "dictionnaires"
    Ceux-ci utilisent généralement une sorte de codage de hachage, de sorte qu'ils auront des performances O(1), mais ne seront rentables que lorsqu'ils seront remplis avec beaucoup plus d'éléments que ceux généralement utilisés.

  • Itérateurs
    Ceux-ci sont justifiés comme étant éventuellement optimisés en un code en ligne efficace, alors qu'il est rarement vérifié pour voir s'ils le sont réellement.

  • Notifications et gestion des événements comme moyen de garder les données cohérentes
    . Cependant, il existe une grande différence entre immédiateté et efficacité. De plus, les "propriétés", lors de l'obtention ou de la définition, sont encouragées à pénétrer profondément dans la structure de données pour essayer de la garder cohérente. Ces méthodes de "laisse courte" peuvent entraîner de grandes quantités de calculs inutiles. Les méthodes de "laisse longue", comme le fait de parcourir périodiquement la structure de données pour la "réparer", peuvent être un peu moins "immédiates" mais beaucoup plus efficaces.

Exemples

3
Mike Dunlavey

Peut-être qu'un simple coup d'œil rapide sur le système vous aidera à identifier les goulots d'étranglement possibles.

"Cette partie n'a pas besoin d'être rapide" (archivage des journaux) "Cette partie doit être très rapide" (accepter de nouvelles connexions)

Ensuite, les pièces très rapides n'ont généralement pas besoin d'être optimisées avec des bizarreries sales, un matériel généralement décent et des pièces bien codées suffiront.

Je réponds simplement à la simple question "Est-ce que je gagne quelque chose à avoir cette partie de code très rapidement?" sera une excellente ligne directrice. Je veux dire, utiliser le bon sens optimise les autres parties du projet!

3
Eric
while true; do echo 3 > /proc/sys/vm/drop_caches; sleep 3600; done

Cela a amené le noyau à passer du temps à vider le cache disque, et une fois qu'il a réussi, tout s'est déroulé lentement jusqu'à ce que le cache soit repeuplé. Causé par une mauvaise compréhension du fait que le cache disque empêchait la mémoire d'être disponible pour les applications à utiliser.

3
dhasenan

N'offense personne, mais je viens de noter une tâche (Java) qui avait cette

import Java.lang.*;
3
Overflown

Une autre astuce de performance fantaisiste :)

if (!loadFromDb().isEmpty) {
    resultList = loadFromDb();
    // do something with results
}

Pour un petit prix de hit DB supplémentaire, vous économisez tout ce temps en faisant comme 10 lignes de code, ce qui ne ferait probablement pas grand-chose de toute façon. Et des choses comme ça étaient dispersées partout dans le code :)

2
Slartibartfast

Une entreprise que j'ai visitée en tant que consultant il y a plusieurs années avait écrit une fonction de tri qui ressemblait à ceci:

procedure sort(string[] values, string direction)
begin
  while not sorted do
  begin
    for every value in values
    begin
      if direction="Ascending" then
      begin
        ... swap values in ascending order
      end
      else if direction="Descending" then
      begin
        ... swap values in descending order
      end
    end;
  end;
end;

C'est un algorithme de bullesort (qui est inefficace en soi) avec un comparaison de chaînes pour la direction dans la boucle intérieure! Je pouvais à peine en croire mes yeux et expliqué que oui, je peux probablement apporter quelques améliorations de vitesse ici (ils étaient mes clients après tout, donc je devais être diplomate sur le fait que c'était le code le plus non optimal que j'aie jamais vu :-))

2
Ville Krumlinde

Un collègue a dû vérifier l'accès à la page pour un rôle spécifique - "Admin" uniquement. Voici ce qu'elle a écrit:

.

if( CurrentUser.CurrentRole == "Role1" || CurrentUser.CurrentRole == "Role2")  
{
// Access denied
} 
else
{
// Access granted
}

au lieu de

if( !CurrentUser.CurrentRole.equals("Admin") ) 
{
 // access denied
}  

Ainsi, chaque fois qu'un nouveau rôle était ajouté au système, le nouveau rôle avait accès à toutes les pages confidentielles.


Le même collègue a également été joint pour la table de production et d'archivage pour toutes les requêtes.

2
Abhishek

Certains de mes collègues, qui étaient sur un projet "d'optimisation" de lots côté serveur existants (écrits en C++), "ont optimisé" à mort la classe de journalisation (!), En utilisant du code et des fonctions spécifiques à win32.

Peut-être que le goulot d'étranglement était dans logger.write (...), qui sait ...

2
Gabriele D'Antona

Beaucoup de programmeurs ne connaissent pas ou ne veulent pas connaître SQL, ils trouvent donc des "trucs" pour éviter d'utiliser vraiment SQL afin de pouvoir placer les données dans un tableau. Les tableaux rendent certaines personnes heureuses. (J'adore les curseurs et les tableaux. Coke et Pepsi.) J'ai trouvé ces deux blocs de code dans le code de quelques programmeurs orientés objet qui se plaignaient de la lenteur des bases de données relationnelles. (la réponse n'est pas plus de mémoire ni plus de processeurs.)

Dans ce cas, la table est une énorme table avec le uniqueid_col est un identifiant unique ou une ligne unique.

Chargez ces données dans arrayX (car les tableaux doivent être plus rapides)

 Sélectionnez uniqueid_col, col2, col3 
 Dans super_big_tbl 
 

(code pseudo)


Loop 
   arrayX.next_record
    if uniqueid_col = '829-39-3984'
      return col2
    end if
end loop
 

(Ma réponse est en bas.)

Cette prochaine est une simple erreur que j'ai également vue. L'idée est que vous n'obtenez jamais un double de cette façon:

 Sélectionnez uniqueid_col, col2, col3 
 Dans le groupe super_big_tbl 
 Par uniqueid_col, col2, col3 
 Ayant uniqueid_col = '829-39-3984' 

La syntaxe correcte doit être

 Sélectionnez uniqueid_col, col2, col3 
 Dans super_big_tbl 
 Où uniqueid_col = '829-39-3984' 
 
1
Stradas

J'allais mentionner StringBuilder pour les concats de chaîne minuscules/sans boucle, mais cela a été mentionné.

Placer les variables d'une méthode dans des membres de classe privés pour les empêcher d'obtenir "des ordures collectées à chaque exécution de la méthode". Les variables sont des types de valeur.

1
Jeremy

Une application qui a utilisé un champ Entier pour allouer au niveau du bit à quels groupes d'accès à l'application nos clients pourraient ajouter leurs utilisateurs. Cela signifiait à l'époque que nous pouvions créer un grand total de 32 groupes à partager entre les 500+ clients.

Aaaah, mais une comparaison au niveau du bit est plus rapide qu'un égal et waaay plus rapide qu'une jointure, n'est-ce pas?

Malheureusement, quand j'ai complètement (et plutôt vocalement) flippé à ce code et à son auteur, j'ai découvert que l'auteur était mon patron. Un mec plutôt autoritaire, il s'avère.

P.s.

Je sais à quoi tu penses, ça aurait dû être une chaîne binaire non? :)

1
Nat

J'en ai un intentionnel ... J'ai déjà implémenté le tri par retour en arrière ... juste comme une preuve de concept;)) inutile de dire que ses performances étaient horribles.

0
luvieere

Tout effort d'optimisation significatif qui n'est pas basé sur des rapports triés à partir d'un outil de profilage gagne un gros WTF de ma part.

0
JeffH