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


Answers

Je pense que l'expression "l'optimisation prématurée est la racine de tout le mal" est une façon, bien plus utilisée. Pour de nombreux projets, il est devenu une excuse pour ne pas prendre en compte la performance avant 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 lorsque les gens devraient vraiment dire: «Eh bien, nous n'y avons pas vraiment pensé à l'avance et nous n'avons pas le temps de le faire maintenant».

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

  • Lecture de la même clé de registre des milliers (ou dizaines de milliers) de fois au cours du lancement du programme.
  • Chargement de la même DLL des centaines ou des milliers de fois
  • Perdre des méga octets de mémoire en conservant inutilement les chemins complets vers les fichiers
  • Ne pas organiser les structures de données pour qu'elles occupent plus de mémoire qu'elles n'en ont besoin
  • Dimensionnement de toutes les chaînes stockant les noms de fichiers ou les chemins d'accès à MAX_PATH
  • Sondage gratuit pour les choses qui ont 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 mesure et compréhension n'est pas une optimisation du tout - c'est juste un changement aléatoire".

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

Question

Nous savons tous que l'optimisation prématurée est la racine de tous les maux, car elle conduit à un code illisible / non supportable. Pire encore, quand quelqu'un met en place une «optimisation» parce qu'elle pense que ce sera plus rapide, mais cela finit par être plus lent, en étant bogué, non supportable, etc. Quel est l'exemple le plus ridicule que vous avez vu? ?




Je pense qu'il n'y a pas de règle absolue: certaines choses sont 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 en tant que valeurs absolues (flottants), mais en tant que décalages par rapport à l'angle «nord-ouest» d'une zone «actuelle». Nous avons dû déballer toutes les données avant de pouvoir l'utiliser. Mais je pense que ce n'est pas une pessimisation, c'est une 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 devraient être formatées en un document XML très lisible, et stockées dans notre base de données en tant que telles (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 que «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, donc vous pouvez imaginer à quel point la performance est devenue horrible (BTW, l'entreprise a fait faillite).

Donc, 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.




J'ai vu des gens utiliser alphadrive-7 pour incuber complètement CHX-LT. C'est une pratique rare. La pratique la plus courante consiste à initialiser le transformateur ZT de sorte que la tamponnage soit réduit (en raison d'une plus grande résistance à la surcharge nette) et à créer des bytegraphications de style java.

Totalement pessimiste!




J'ai vu une fois une base de données MSSQL qui utilisait une table 'Root'. La table racine comportait quatre colonnes: GUID (identificateur unique), ID (int), LastModDate (datetime) et CreateDate (datetime). Toutes les tables de la base de données étaient Foreign Key'd à la table racine. Chaque fois qu'une nouvelle ligne était créée dans une table de la base de données, vous deviez utiliser une ou deux procédures stockées pour insérer une entrée dans la table racine avant de pouvoir accéder à la table dont vous vous souciez (plutôt que la base de données vous avec quelques déclencheurs simples déclencheurs).

Cela a créé un désordre d'inutile entendu et des maux de tête, exigé tout écrit dessus pour utiliser sprocs (et éliminer mes espoirs de présenter LINQ à l'entreprise.Il était possible mais ne vaut pas la peine d'être mal à la tête), et pour couronner le tout 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 beaucoup d'espace car nous n'utilisions pas Guids sur les tables (mais ... n'est pas un GUID généré dans la table Root pour chaque ligne que nous faisons?) , amélioré la performance en quelque sorte, et rendu «facile» d'auditer les modifications apportées à la base de données.

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




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




Utiliser une regex pour diviser une chaîne quand un simple string.split suffit




Très tard à 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();
}

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




J'ai déjà travaillé sur une application qui était pleine de code comme ceci:

 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 pour:

            return curr;

Doublé la performance de l'application.




Le grand numéro un que je rencontre à maintes reprises 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 interne: IL NE SE PRODUIRA PAS!




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

Comme je l'ai creusé, j'ai trouvé que le programmeur original 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 sens que tous les threads nécessitaient une ressource partagée, sur laquelle ils étaient dans une impasse. Bien sûr, tous les avantages de la concurrence ont disparu. De plus, il s'est écrasé sur la plupart des systèmes pour lancer plus de 100 threads, mais tous sauf un d'entre eux se verrouillent. Ma machine de développement costaud était une exception dans la mesure où elle faisait tourner un jeu de données de 150 sources en environ 6 heures.

Donc, pour le réparer, j'ai supprimé les composants multi-thread et nettoyé les E / S. En l'absence d'autres modifications, le temps d'exécution sur l'ensemble de données 150-source a chuté en dessous de 10 minutes sur ma machine, et de l'infini à moins d'une demi-heure sur la machine de la société moyenne.




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

Insister sur le fait de lancer 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 qu'on vous les a signalées).




Cela ne correspond pas exactement à la question, mais je vais le mentionner de toute façon un récit édifiant. Je travaillais sur une application distribuée qui fonctionnait lentement, et j'ai atterri à DC pour assister à une réunion visant principalement à résoudre le problème. Le responsable du projet a commencé à définir une nouvelle architecture visant à résoudre le retard. Je me suis porté volontaire pour dire que j'avais pris quelques mesures durant le week-end pour isoler le goulot d'étranglement à une seule méthode. Il s'est avéré qu'il y avait un enregistrement manquant sur une recherche locale, ce qui obligeait l'application à se rendre à un serveur distant à chaque transaction. En ajoutant l'enregistrement au magasin local, le délai a été éliminé - le problème a été résolu. Notez que la réarchitecture n'aurait pas résolu le problème.




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'effort que si vous l'aviez ajouté au début. Si vous créez un programme réussi, les chances sont que vous allez en avoir besoin. Si vous avez l'habitude de créer des programmes dont la vie s'épuise rapidement alors continuez à pratiquer YAGNI parce que je suppose que YAGNI.




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

Une "optimisation" qu'ils ont faite était de ne pas allouer plus de mémoire que nécessaire. Apparemment, ne réalisant pas que la raison pour laquelle des classes comme std::string allouent de la mémoire en trop est qu'une séquence d'opérations += peut s'exécuter en temps O (n).

Au lieu de cela, chaque appel += forcé une réallocation, qui a été répétée pour être ajoutée à un algorithme de O (n²) Schlemiel le peintre .




Aucune infraction à personne, mais je GradeD une mission (java) qui avait cette

import java.lang.*;