web-dev-qa-db-fra.com

La STL doit-elle être évitée dans les grandes applications?

Cela peut sembler une question étrange, mais dans mon département, nous avons des problèmes avec la situation suivante:

Nous travaillons ici sur une application serveur, qui s'agrandit de plus en plus, même au moment où nous envisageons de la diviser en différentes parties (fichiers DLL), en chargeant dynamiquement en cas de besoin et en déchargeant ensuite, afin de pouvoir gérer les problèmes de performance.

Mais: les fonctions que nous utilisons passent des paramètres d'entrée et de sortie en tant qu'objets STL, et comme mentionné dans une réponse Stack Overflow , c'est une très mauvaise idée. (Le message contient quelques ± solutions et hacks, mais tout ne semble pas très solide.)

Évidemment, nous pourrions remplacer les paramètres d'entrée/sortie par des types C++ standard et créer des objets STL à partir de ceux une fois à l'intérieur des fonctions, mais cela pourrait entraîner des baisses de performances.

Est-il correct de conclure que, si vous envisagez de créer une application, qui pourrait devenir si grande qu'un seul PC ne peut plus la gérer, vous ne devez pas du tout utiliser STL en tant que technologie?

Plus d'informations sur cette question:
Il semble y avoir des malentendus sur la question: le problème est le suivant:
Mon application utilise une énorme quantité de performances (CPU, mémoire) afin de terminer son travail, et je voudrais diviser ce travail en différentes parties (car le programme est déjà divisé en plusieurs fonctions), ce n'est pas difficile de créer des DLL à partir de mon application et de placer certaines des fonctions dans la table d'exportation de ces DLL. Cela entraînerait la situation suivante:

+-----------+-----------+----
| Machine1  | Machine2  | ...
| App_Inst1 | App_Inst2 | ...
|           |           |    
| DLL1.1    | DLL2.1    | ...
| DLL1.2    | DLL2.2    | ...
| DLL1.x    | DLL2.x    | ...
+-----------+-----------+----

App_Inst1 est l'instance de l'application, installée sur Machine1, tandis que App_Inst2 est l'instance de la même application, installée sur Machine2.
DLL1.x est une DLL, installée sur Machine1, tandis que DLL2.x est une DLL, installée sur Machine2.
DLLx.1 couvre la fonction exportée1.
DLLx.2 couvre la fonction exportée2.

Maintenant, sur Machine1, j'aimerais exécuter function1 et function2. Je sais que cela surchargera Machine1, donc j'aimerais envoyer un message à App_Inst2, demandant à cette instance d'application d'exécuter function2.

Les paramètres d'entrée/sortie de function1 et function2 sont des objets STL (C++ Standard Type Library), et régulièrement je pourrais m'attendre à ce que le client fasse des mises à jour d'App_Inst1, App_Inst2, DLLx.y (mais pas tous, le client peut mettre à niveau Machine1 mais pas Machine2, ou seulement mettre à niveau les applications mais pas les DLL ou vice versa, ...). Évidemment, si l'interface (paramètres d'entrée/sortie) change, le client est obligé de procéder à des mises à niveau complètes.

Cependant, comme mentionné dans l'URL StackOverflow référencée, une simple recompilation d'App_Inst1 ou de l'une des DLL peut entraîner l'effondrement de l'ensemble du système, d'où mon titre d'origine de cet article, déconseillant l'utilisation de STL (C++ Standard Template Library) pour les grandes applications.

J'espère que par la présente, j'ai dissipé certaines questions/doutes.

25
Dominique

Il s'agit d'un problème classique X-Y froid comme la pierre.

Votre réel problème est lié aux performances. Cependant, votre question indique clairement que vous n'avez effectué aucun profilage ni aucune autre évaluation de l'origine des problèmes de performances. Au lieu de cela, vous espérez que le fractionnement de votre code en DLL résoudra comme par magie le problème (ce qu'il ne fera pas, pour mémoire), et maintenant vous vous inquiétez d'un aspect de cette non-solution.

Au lieu de cela, vous devez résoudre le vrai problème. Si vous avez plusieurs exécutables, vérifiez lequel est à l'origine du ralentissement. Pendant que vous y êtes, assurez-vous que c'est votre programme qui prend tout le temps de traitement, et non un pilote Ethernet mal configuré ou quelque chose comme ça. Et après cela, commencez à profiler les différentes tâches de votre code. La minuterie de haute précision est votre amie ici. La solution classique consiste à surveiller les temps de traitement moyens et les pires cas pour un morceau de code.

Lorsque vous avez des données, vous pouvez déterminer comment résoudre le problème, puis déterminer où optimiser.

111
Graham

Si vous devez diviser un logiciel entre plusieurs machines physiques, vous devez avoir une certaine forme de sérialisation lors du transfert de données entre machines car seulement dans certains cas, vous pouvez simplement envoyer le même binaire exact entre machines. La plupart des méthodes de sérialisation n'ont aucun problème à gérer les types STL, donc ce cas ne m'inquiète pas.

Si vous devez diviser une application en bibliothèques partagées (DLL) (avant de le faire pour des raisons de performances, vous devez vraiment vous assurer que cela résoudrait réellement vos problèmes de performances) la transmission d'objets STL peut être un problème mais ne doit pas l'être. Comme le lien que vous avez fourni décrit déjà, le passage d'objets STL fonctionne si vous utilisez le même compilateur et les mêmes paramètres de compilateur. Si les utilisateurs fournissent les DLL, vous ne pourrez peut-être pas facilement compter sur cela. Cependant, si vous fournissez toutes les DLL et compilez tout ensemble, vous pourrez peut-être compter dessus et utiliser des objets STL à travers DLL les limites deviennent tout à fait possibles. Vous devez toujours faire attention à votre les paramètres du compilateur afin que vous n'obteniez pas plusieurs tas différents si vous transmettez la propriété de l'objet, bien que ce ne soit pas un problème spécifique à la STL.

38
Pierre Andersson

Nous travaillons ici sur une application serveur, qui grandit de plus en plus, même au moment où nous envisageons de la diviser en différentes parties (DLL), de charger dynamiquement en cas de besoin et de décharger ensuite, afin de pouvoir gérer les les problèmes de performance

La RAM est bon marché et donc le code inactif est bon marché. Le chargement et le déchargement de code (en particulier le déchargement) est un processus fragile et il est peu probable qu'il ait un effet significatif sur les performances de vos programmes sur du matériel de bureau/serveur moderne.

Le cache est plus cher, mais cela n'affecte que le code récemment actif, pas le code restant en mémoire inutilisé.

En général, les programmes dépassent leurs ordinateurs en raison de la taille des données ou du temps processeur, et non de la taille du code. Si la taille de votre code devient si grande qu'elle cause des problèmes majeurs, vous voudrez probablement chercher pourquoi cela se produit en premier lieu.

Mais: les fonctions que nous utilisons passent des paramètres d'entrée et de sortie en tant qu'objets STL, et comme mentionné dans cette URL StackOverflow, c'est une très mauvaise idée.

Cela devrait être correct tant que les DLL et l'exécutable sont tous construits avec le même compilateur et liés dynamiquement à la même bibliothèque d'exécution C++. Il s'ensuit que si l'application et ses DLL associées sont construites et déployées comme une seule unité, cela ne devrait pas poser de problème.

Lorsque cela peut devenir un problème, c'est lorsque les bibliothèques sont construites par différentes personnes ou peuvent être mises à jour séparément.

Est-il permis de conclure que, si vous envisagez de créer une application, qui pourrait devenir si grande qu'un seul PC ne peut plus la gérer, vous ne devez pas du tout utiliser STL en tant que technologie?

Pas vraiment.

Une fois que vous avez commencé à répartir une application sur plusieurs machines, vous avez toute une série de considérations sur la façon dont vous passez les données entre ces machines. Les détails sur l'utilisation de types STL ou de types plus basiques risquent d'être perdus dans le bruit.

20
Peter Green

Non, je ne pense pas que cette conclusion s'ensuit. Même si votre programme est distribué sur plusieurs machines, il n'y a aucune raison que l'utilisation de la STL en interne vous oblige à l'utiliser dans la communication entre modules/processus.

En fait, je dirais que vous devriez séparer la conception des interfaces externes de l'implémentation interne dès le départ, car les premières seront plus solides/difficiles à changer par rapport à ce qui est utilisé en interne

13
Bwmat

Vous manquez le point de cette question.

Il existe essentiellement deux types de DLL. Le vôtre et celui de quelqu'un d'autre. Le "problème STL" est que vous et eux n'utilisez peut-être pas le même compilateur. Évidemment, ce n'est pas un problème pour votre propre DLL.

7
MSalters

Si vous créez les DLL à partir de la même arborescence source en même temps avec le même compilateur et les mêmes options de génération, cela fonctionnera correctement.

Cependant, la manière "aromatisée Windows" de diviser une application en plusieurs parties dont certaines sont réutilisables est les composants COM . Ceux-ci peuvent être petits (contrôles individuels ou codecs) ou grands (IE est disponible en tant que contrôle COM, dans mshtml.dll).

chargement dynamique en cas de besoin et déchargement par la suite

Pour une application serveur, cela aura probablement une efficacité terrible ; ce n'est vraiment viable que si vous avez une application qui passe par plusieurs phases sur une longue période de temps afin que vous sachiez quand quelque chose ne sera plus nécessaire. Cela me rappelle les jeux DOS utilisant le mécanisme de superposition.

En outre, si votre système de mémoire virtuelle fonctionne correctement, il s'en occupera en paginant les pages de codes inutilisées.

pourrait devenir si grand qu'un seul PC ne peut plus le gérer

Achetez un PC plus grand.

N'oubliez pas que avec la bonne optimisation, un ordinateur portable peut surpasser un cluster hadoop.

Si vous avez vraiment besoin de plusieurs systèmes, vous devez réfléchir très attentivement à la frontière entre eux, car c'est là que se situe le coût de sérialisation. C'est là que vous devriez commencer à regarder des frameworks comme MPI.

5
pjc50

Nous travaillons ici sur une application serveur, qui s'agrandit de plus en plus, même au moment où nous envisageons de la diviser en différentes parties (fichiers DLL), en chargeant dynamiquement en cas de besoin et en déchargeant ensuite, afin de pouvoir gérer les problèmes de performance.

La première partie est logique (fractionnement de l'application sur différentes machines, pour des raisons de performances).

La deuxième partie (chargement et déchargement des bibliothèques) n'a pas de sens, car c'est un effort supplémentaire à faire, et cela n'améliorera pas (vraiment) les choses.

Le problème que vous décrivez est mieux résolu avec des machines de calcul dédiées, mais celles-ci ne devraient pas fonctionner avec la même application (principale).

La solution classique ressemble à ceci:

[user] [front-end] [machine1] [common resources]
                   [machine2]
                   [machine3]

Entre les machines frontales et de calcul, vous pouvez avoir des choses supplémentaires, telles que des équilibreurs de charge et une surveillance des performances, et le traitement spécialisé sur des machines dédiées est bon pour la mise en cache et les optimisations de débit.

Cela n'implique en aucun cas un chargement/déchargement supplémentaire des DLL, ni rien à voir avec la STL.

C'est-à-dire tilisez STL en interne si nécessaire et sérialisez vos données entre les éléments (voir grpc et tampons de protocole et le type de problèmes qu'ils résolvent).

Cela dit, avec les informations limitées que vous avez fournies, cela ressemble au problème classique x-y (comme l'a dit @Graham).

0
utnapistim