Qu'est-ce qui ralentit un produit logiciel volumineux et complexe? [fermé]

16

Pour une raison qui est largement hors de propos, j'ai installé Delphi 7 à nouveau en si longtemps. Je dois dire que j'ai été complètement époustouflé - d'une certaine manière, je ne l'ai pas été depuis un certain temps. Ce n'est pas du tout ainsi que je me souviens des choses. L'installation a duré environ 30 secondes. Son lancement a pris 2 secondes et il a été immédiatement utilisable. Je peux appuyer sur "Exécuter" la seconde après son démarrage, et moins d'une seconde plus tard, le programme vierge est déjà visible et en cours d'exécution. Vive les ordinateurs tellement plus rapides!

Mais la raison pour laquelle j'ai été époustouflé comme ceci est parce que j'utilise généralement Visual Studio 2010, cela ne semble pas du tout accrocheur. Certes, Delphi 7 est un système beaucoup plus petit que Visual Studio 2010, mais il a l' apparence d'avoir toutes les choses là - bas vraiment nécessaire: une palette de commande, un concepteur de formulaire, un éditeur de code avec la complétion de code. Je me rends compte que le langage pourrait être plus simple, et l'achèvement du code pourrait être beaucoup moins puissant, et l'EDI pourrait ne pas être presque aussi extensible et riche en fonctionnalités, mais quand même: je ne comprends pas comment (c'est-à-dire par quel mécanisme) fait avoir de nombreuses fonctionnalités supplémentaires (que je n'ai peut-être même pas encore déclenchées) font qu'un système comme Visual Studio se sent toujours lent en comparaison.

Je voudrais demander aux personnes expérimentées dans l'utilisation de systèmes à l'échelle de Visual Studio: qu'est-ce qui les ralentit? S'agit-il des couches sur les couches d'abstractions nécessaires pour maintenir la base de code dans les capacités de compréhension humaine? Est-ce la quantité de code à parcourir? Est-ce la tendance moderne vers des approches permettant de gagner du temps au programmeur aux dépens (énormément énormes) des cycles d'horloge / du service d'utilisation de la mémoire?

Roman Starkov
la source
7
Simple: à mesure que la masse augmente, il faut plus de force pour surmonter l'inertie.
Shog9
Quelqu'un m'a dit une fois les managers mais je n'y crois pas du tout.
MIchael Grassman
1
C'est une grande partie de la raison pour laquelle j'utilise toujours principalement D7 pour la programmation Delphi.
GrandmasterB
Le code le plus rapide est celui qui n'est jamais exécuté.
Henry
4
@romkyns: Je trouve que beaucoup de logiciels à l'ère moderne sont souvent incroyablement gonflés, inutilement volumineux et peu maniables. Une grande partie du logiciel résout désormais les mêmes problèmes qui ont été résolus il y a dix, voire vingt ans, avec une fraction de la puissance et de l'espace. Pourquoi at-il encore autant de retard que jamais, sinon plus? Inefficacité et ballonnement.
Orbling le

Réponses:

20

Astronautique architecturale

Visual Studio 2010 est basé sur Windows Presentation Foundation. Jetez un œil à la classe Button pour WPF. Il s'agit du 9e enfant d'une classe de base. Il a environ 5 pages de propriétés, méthodes et événements. Dans les coulisses, il a cinq autres pages de définitions de style qui décrivent ses coins magnifiquement arrondis et les transitions d'animation subtiles lorsqu'un curseur de la souris se déplace dessus. C'est tout pour quelque chose qui affiche fondamentalement du texte ou une image et produit un événement de clic lorsqu'il détecte qu'un bouton de la souris descend.

Arrêtez un programme comme Visual Studio à tout moment aléatoire. Regardez la trace de la pile. Il y a de fortes chances que vous ayez 20 niveaux de profondeur dans la pile des appels et que cinq DLL ont été chargées pour y arriver.

Maintenant, comparez ces deux choses avec Delphi. Je parie que vous trouvez qu'un bouton Delphi n'a que 20 propriétés, méthodes et événements. Je parie que l'IDE Delphi n'a qu'une trace de pile de 5-7 niveaux de profondeur. Parce que lorsque les ordinateurs étaient plus lents, vous ne pouviez tout simplement pas supporter les frais généraux de Visual Studio 2010 sans que l'IDE prenne 40 minutes pour démarrer :-)

Est-ce que l'un est meilleur que l'autre? Eh bien, je peux généralement dire à un programme Delphi quand il se charge car il semble plat, les couleurs sont coupées (8 bits peut-être?), Et il n'y a pas d'ombrage ou d'animation subtile. Je me sens juste «bon marché» ces jours-ci. Pas cher, mais rapide.

Sommes-nous mieux lotis? C'est une question pour les philosophes, pas pour les codeurs.

Jay Beavers
la source
4
Un programme Delphi n'a pas l'air plat. Un programmeur programme plutôt un programme pour qu'il soit plat. Vous pouvez créer de belles interfaces modernes et pleines couleurs avec Delphi comme vous le feriez en C # ou C ++.
GrandmasterB
2
Ceci est une réponse perspicace; mais je ne suis pas sûr qu'il soit complet. Visual Studio 2008 (le prédécesseur de 2010) ne contient pas de WPF et est toujours plus lent que Delphi 7. Diriez-vous toujours la même chose à propos de la profondeur de la pile d'appels et du nombre de DLL chargées?
Timwi
3
@ Timwi Oui, absolument je le ferais. Mon point était moins sur les maux de WPF (j'aime WPF en fait) et plus sur la façon dont nous avons tendance à ajouter des couches sur des couches d'abstraction logicielle lorsque nous avons le choix. Peut-être que Visual Studio 2008 n'avait pas autant de frais généraux, mais comme vous l'avez noté, il en avait assez :-)
Jay Beavers
@GrandmasterB, je ne claque pas Delphi car il est livré avec moins d'hypothèses et des bibliothèques plus simples. WPF a été conçu en supposant que l'accélération matérielle du GPU permettrait aux programmes d'utiliser des couleurs plus profondes, des animations fréquentes, un mélange alpha, des ombres, etc. Delphi a été conçu à un moment où ces hypothèses ne pouvaient pas être faites. Pourriez-vous réimplémenter tout cela dans Delphi? Bien sûr, mais vous devrez mettre beaucoup de codage juste pour obtenir le comportement d'un bouton WPF. Du côté positif, un bouton Delphi n'est pas livré avec des exigences de CPU, de mémoire et de GPU qu'un bouton WPF a non plus, ce qui était la question de @ OP.
Jay Beavers
10
Votre argument pour une interface utilisateur plate et simple est complètement invalidé par la nouvelle interface utilisateur `` moderne '' de Windows 10. Maintenant, nous avons tous ces frais généraux afin de créer des boutons plats, carrés et unis comme nous l'avions il y a 30 ans.
gbjbaanb
11

Je voudrais demander aux personnes expérimentées dans l'utilisation de systèmes à l'échelle de Visual Studio: qu'est-ce qui les ralentit? S'agit-il des couches sur les couches d'abstractions nécessaires pour maintenir la base de code dans les capacités de compréhension humaine? Est-ce la quantité de code à parcourir? Est-ce la tendance moderne vers des approches permettant de gagner du temps au programmeur aux dépens (énormément énormes) des cycles d'horloge / du service d'utilisation de la mémoire?

Je pense que vous en avez deviné un certain nombre, mais j'aimerais offrir ce que je considère comme le plus gros facteur, ayant travaillé sur une base de code assez grande (je ne sais pas si elle est aussi grande que Visual Studio - était dans les millions de lignes de code catégorie et environ un millier de plugins) pendant environ 10 ans et des phénomènes d'observation se produisent.

C'est aussi un peu moins controversé car il n'entre pas dans les API ou les fonctionnalités de langage ou quelque chose comme ça. Celles-ci concernent les «coûts» qui peuvent engendrer un débat plutôt que les «dépenses», et je veux me concentrer sur les «dépenses».

Coordination lâche et héritage

Ce que j'ai observé, c'est qu'une mauvaise coordination et un long héritage ont tendance à entraîner beaucoup de déchets accumulés.

Par exemple, j'ai trouvé une centaine de structures d'accélération dans cette base de code, dont beaucoup sont redondantes.

Nous aimerions avoir un arbre KD pour accélérer un moteur physique, un autre pour un nouveau moteur physique qui fonctionnait souvent en parallèle avec l'ancien, nous aurions des dizaines d'implémentations d'octrees pour divers algorithmes de maillage, un autre arbre KD pour le rendu , cueillette, etc., etc., etc. Ce sont toutes de grandes structures d'arbres volumineuses utilisées pour accélérer les recherches. Chaque individu peut prendre des centaines de mégaoctets en gigaoctets de mémoire pour une entrée de taille très moyenne. Ils n'étaient pas toujours instanciés et utilisés tout le temps, mais à tout moment, 4 ou 5 d'entre eux pouvaient être en mémoire simultanément.

Maintenant, tous ces éléments stockaient exactement les mêmes données pour accélérer leur recherche. Vous pouvez l'imaginer comme une ancienne base de données analogique qui stocke tous ses champs dans 20 cartes / dictionnaires / arbres B + redondants différents à la fois, organisés de manière identique par les mêmes clés, et les recherche tout le temps. Maintenant, nous prenons 20 fois plus de mémoire et de traitement.

De plus, en raison de la redondance, il y a peu de temps pour optimiser l'un d'eux avec le prix de maintenance qui l'accompagne, et même si nous le faisions, cela n'aurait que 5% de l'effet idéalement.

Qu'est-ce qui cause ce phénomène? Une mauvaise coordination était la cause numéro un que j'ai vue. Beaucoup de membres de l'équipe travaillent souvent dans leurs écosystèmes isolés, développant ou utilisant des structures de données tierces, mais n'utilisant pas les mêmes structures que les autres membres de l'équipe utilisaient même s'ils étaient des doublons flagrants des mêmes préoccupations.

Qu'est-ce qui fait que ce phénomène persiste? L'héritage et la compatibilité étaient la cause numéro un que j'ai vue. Comme nous avons déjà payé le coût de la mise en œuvre de ces structures de données et que de grandes quantités de code dépendaient de ces solutions, il était souvent trop risqué d'essayer de les consolider en moins de structures de données. Même si bon nombre de ces structures de données étaient très redondantes sur le plan conceptuel, elles n'étaient pas toujours proches de l'identique dans leur conception d'interface. Les remplacer aurait donc été un grand changement risqué au lieu de les laisser consommer de la mémoire et du temps de traitement.

Efficacité de la mémoire

En règle générale, l'utilisation de la mémoire et la vitesse ont tendance à être liées au moins au niveau global. Vous pouvez souvent repérer les logiciels lents par la façon dont ils accaparent la mémoire. Ce n'est pas toujours vrai que plus de mémoire conduit à un ralentissement, car ce qui compte, c'est la mémoire «chaude» (quelle mémoire est consultée en permanence - si un programme utilise une charge de mémoire mais seulement 1 mégaoctet est utilisé tout le temps, alors ce n’est pas un problème de vitesse).

Vous pouvez donc repérer les porcs potentiels en fonction de l'utilisation de la mémoire la plupart du temps. Si une application prend des dizaines à des centaines de mégaoctets de mémoire au démarrage, elle ne sera probablement pas très efficace. Des dizaines de mégaoctets peuvent sembler petits lorsque nous avons des gigaoctets de DRAM de nos jours, mais les caches de processeur les plus grands et les plus lents sont toujours dans la plage de mégaoctets minables, et les plus rapides sont toujours dans la plage de kilo-octets. Par conséquent, un programme qui utilise 20 mégaoctets juste pour démarrer et ne rien faire utilise en fait assez "beaucoup" de mémoire du point de vue du cache du processeur matériel, surtout si les 20 mégaoctets de cette mémoire seront accessibles à plusieurs reprises et fréquemment pendant l'exécution du programme.

Solution

Pour moi, la solution consiste à rechercher des équipes plus coordonnées et plus petites pour créer des produits, celles qui peuvent en quelque sorte suivre leurs "dépenses" et éviter "d'acheter" les mêmes articles encore et encore et encore.

Coût

Je vais plonger dans le côté «coût» plus controversé juste un tout petit peu avec un phénomène de «dépenses» que j'ai observé. Si un langage finit par avoir un prix inévitable pour un objet (comme celui qui fournit une réflexion à l'exécution et ne peut pas forcer l'allocation contiguë pour une série d'objets), ce prix n'est cher que dans le contexte d'un élément très granulaire, comme un unique Pixelou Boolean.

Pourtant, je vois beaucoup de code source pour les programmes qui gèrent une lourde charge (par exemple: gérer des centaines de milliers à des millions de cas Pixelou des Booleaninstances) payant ce coût à un niveau aussi granulaire.

La programmation orientée objet peut en quelque sorte exacerber cela. Pourtant, ce n'est pas le coût des "objets" en soi ou même la POO en faute, c'est simplement que ces coûts sont payés à un niveau si granulaire d'un élément minuscule qui va être instancié par des millions.

Voilà donc les autres phénomènes de «coûts» et de «dépenses» que j'observe. Le coût est de quelques sous, mais les sous s'additionnent si nous achetons un million de canettes de soda individuellement au lieu de négocier avec un fabricant pour un achat en gros.

La solution ici pour moi est l'achat "en gros". Les objets sont parfaitement bien, même dans les langues qui ont une étiquette de prix pour chacun à condition que ce coût ne soit pas payé individuellement un million de fois pour l'équivalent analogique d'une canette de soda.

Optimisation prématurée

Je n'ai jamais vraiment aimé le libellé utilisé par Knuth ici, car "l'optimisation prématurée" accélère rarement les programmes de production dans le monde réel. Certains interprètent cela comme «l'optimisation précoce», alors que Knuth signifiait davantage «l'optimisation sans les connaissances / l'expérience appropriées pour connaître son véritable impact sur le logiciel». Si quoi que ce soit, l'effet pratique d' une véritable optimisation prématurée va souvent au logiciel de faire plus lent , car la dégradation des moyens de maintenabilité , il y a peu de temps pour optimiser les chemins critiques qui comptent vraiment .

C'est le dernier phénomène que j'ai observé, où les développeurs tentaient d'économiser des sous sur l'achat d'une seule canette de soda, pour ne plus jamais acheter, ou pire, une maison, perdaient tout leur temps à pincer des sous (ou pire, des sous imaginaires de ne pas comprendre leur compilateur ou l'architecture du matériel) alors qu'il y avait des milliards de dollars dépensés inutilement ailleurs.

Le temps est très limité, donc essayer d'optimiser les absolus sans avoir les informations contextuelles appropriées nous prive souvent de la possibilité d'optimiser les endroits qui comptent vraiment, et donc, en termes d'effet pratique, je dirais que "l'optimisation prématurée rend le logiciel beaucoup plus lent. "

Le problème est qu'il existe des types de développeurs qui prendront ce que j'ai écrit ci-dessus à propos des objets et essaieront d'établir une norme de codage qui interdit la programmation orientée objet ou quelque chose de fou de ce genre. Une optimisation efficace est une priorisation efficace, et cela ne vaut absolument rien si nous nous noyons dans une mer de problèmes de maintenance.

Thomas Owens
la source
2
La dette technique, en d'autres termes. Dette technique jamais remboursée.
Robert Harvey
1
Robert a raison. Une erreur d'un gars, deux cents erreurs --forcepar des managers qui crient "vous serez viré si vous ne l'implémentez pas d'ici demain" qui emportent des années de bonnes pratiques d'ingénierie logicielle, de TDD, de tests unitaires et de tout principe de programmation humain et sain , plus deux autres fois où vous étiez fatigué .. ce gars qui a quitté l'entreprise fou parce qu'il a été licencié sans raison et a foiré la base de code .. ces bibliothèques abandonnées que vous n'avez jamais mises à jour ... et vous l'avez: délicieuse base de code spaghetti et les logiciels gonflés. Bon appétit
user3834459
2
Intéressant, en particulier dans la façon dont vous avez vu une granularité excessive mal utilisée. Je me suis surpris à faire quelque chose de similaire à l'occasion dans le passé et j'ai obtenu de mauvaises performances. Ceci est assez similaire à votre réponse d'il y a quelques jours sur l'utilisation des collections et des algorithmes en vrac de préférence à une granularité excessive . Je ne peux pas croire que cette réponse n'ait pas été plus appréciée pour sa profondeur. Cela me fait repenser plusieurs des conceptions que j'ai construites au fil des ans. Je me demande pourquoi ces techniques ne sont pas plus largement promues?
Mike soutient Monica le
2
@ Mike, je suis un peu un record lorsqu'il s'agit de promouvoir davantage un état d'esprit orienté données. Il est populaire dans l'industrie du jeu vidéo où il essaie d'utiliser chaque centimètre du matériel. Cela dit, cela réduit certes la flexibilité. Si vous avez une classe de pixels abstraits, vous pouvez faire des choses folles avec comme une seule image qui mélange deux ou plusieurs formats de pixels différents! Pourtant, lorsque nous avons affaire à des chemins critiques, aucune image ne bénéficierait probablement de ce niveau de flexibilité, et les performances commencent à devenir une véritable préoccupation pour tout ce qui concerne les images et les pixels.
1
Dans le mauvais vieux temps, j'ai implémenté du code pour contourner les API graphiques et accéder directement aux pixels en mémoire pour une partie critique de mon code. La différence entre les nombreuses couches d'abstraction et d'accès direct était quelque chose comme 100x, qui comptait sur un ordinateur à l'époque. Maintenant, vos ordinateurs sont assez rapides pour pouvoir vous abattre sur n'importe quelle quantité d'abstraction, si vous le devez.
Michael Shopsin