11
votes

Si un programmeur se soucie vraiment du nombre et / ou de la fréquence à laquelle les objets sont créés dans .NET?

Cette question me perturbe depuis longtemps maintenant. Je viens d'un fond lourd et long C ++, et depuis que j'ai commencé à programmer dans C # et à faire face à la collecte des ordures, j'ai toujours eu le sentiment que cette "magie" viendrait à un coût.

J'ai récemment commencé à travailler dans un grand projet MMO écrit en Java (côté serveur). Ma tâche principale est d'optimiser l'utilisation de la composition de la mémoire et de la CPU. Des centaines de milliers de messages par seconde sont en cours d'envoi et la même quantité d'objets sont également créés. Après beaucoup de profilage, nous avons découvert que le collectionneur de la poubelle VM mangeait beaucoup de temps de processeur (en raison de collections constantes) et a décidé d'essayer de minimiser la création d'objets, en utilisant des pools, le cas échéant et la réutilisation de tout ce que nous pouvons. Cela s'est avéré être une très bonne optimisation jusqu'à présent.

Donc, d'après ce que j'ai appris, avoir un collectionneur à ordures est génial, mais vous ne pouvez pas simplement prétendre que cela n'existe pas, et vous n'existez pas encore Besoin de prendre soin de la création d'objets et de ce qu'elle implique (au moins en Java et une grande application comme celle-ci).

donc, est-ce également vrai pour .NET? Si c'est le cas, dans quelle mesure?

J'écris souvent des paires de fonctions telles que celles-ci: xxx

une deuxième fonction est fournie au cas où quelqu'un a déjà enveloppe faite qui peut être réutilisée pour stocker le résultat, mais je trouve cela un peu étrange.

J'écris aussi parfois des structures quand je préférerais utiliser des cours, juste parce que je sais qu'il y aura des dizaines de milliers d'instances étant constamment créées et disposées, et cela me semble vraiment étrange.

Je sais qu'en tant que développeur .NET, je ne devrais pas m'inquiéter de ce genre de problèmes, mais mon expérience avec Java et Common Sens me dit que je devrais.

Toute lumière et toute pensée sur cette question serait très appréciée. Merci d'avance.


0 commentaires

12 Réponses :


1
votes

J'ai la même pensée tout le temps.

La vérité est que nous avons appris à faire attention aux tacts de processeurs inutiles et à la consommation de mémoire, le coût des petites imperfections dans notre code tout simplement négligeable dans la pratique.

Si vous en êtes au courant et toujours regarder, je pense que vous allez bien écrire de code non parfait. Si vous avez commencé avec .NET / Java et n'avez aucune expérience préalable dans la programmation de bas niveau, les chances sont que vous écrivez un code très abusif et inefficace.

Et de toute façon, comme on dit, l'optimisation prématurée est la racine de tout mal. Vous pouvez passer des heures à optimiser une petite fonction et il s'avère alors qu'une autre partie du code donne un goulot d'étranglement. Il suffit de garder l'équilibre le faire simple et de le faire stupidement.


0 commentaires

9
votes

Oui, il est vrai de .NET aussi bien. La plupart d'entre nous ont le luxe d'ignorer les détails de la gestion de la mémoire, mais dans votre cas - ou dans les cas où un volume élevé est Congestionner mémoire - puis certains optimizaition est nécessaire.

Une optimisation que vous pourriez envisager pour votre cas - quelque chose que j'ai pensé à écrire un article sur, en fait - est la combinaison de struct et ref pour l'élimination déterministe réel.

Puisque vous venez d'un arrière-plan C ++, vous savez que dans C ++, vous pouvez instancier un objet, soit sur le tas (en utilisant le nouveau mot-clé et revenir un pointeur) ou sur la pile (par instanciation comme un type primitif, à savoir MyType myType; ). Vous pouvez passer des éléments de pile alloués par référence aux fonctions et méthodes en disant la fonction d'accepter une référence (en utilisant le & mot-clé avant le nom du paramètre dans votre déclaration). Faire cela permet de maintenir votre objet pile alloué en mémoire aussi longtemps que la méthode dans laquelle il a été affecté reste portée du projet; une fois qu'il est hors de portée, l'objet est récupéré, le destructeur est appelé, ba-da-bing, ba-da-boum, yer de Bob l'oncle, et fait sans pointeurs.

J'ai utilisé cette astuce pour créer un code étonnamment dans mes C performant ++ jours - au détriment d'une pile plus grande et le risque d'un débordement de pile, naturellement, mais une analyse minutieuse a réussi à garder ce risque très minime.

Mon point est que vous pouvez faire la même astuce en C # en utilisant struct et refs. Les compromis? En plus du risque d'un débordement de pile si vous ne faites pas attention ou si vous utilisez des objets volumineux, vous êtes limité à aucun héritage, et vous bien deux votre code, ce qui rend moins testable et moins maintenable. De plus, vous avez encore à traiter des questions chaque fois que vous utilisez des appels de bibliothèque de base.

Pourtant, il pourrait être intéressant de regarder-voir dans votre cas.


0 commentaires

5
votes

C'est l'une de ces questions où il est très difficile de réduire une réponse définitive d'une manière qui vous aidera. Le .NET GC est très bien à régler les besoins en mémoire de votre application. C'est bien que votre demande puisse être codée sans que vous ayez besoin de vous inquiéter de la gestion de la mémoire? Je ne sais pas.

Il y a définitivement des choses de bon sens que vous pouvez faire pour vous assurer de ne pas marteler le GC. L'utilisation des types de valeur est définitivement un moyen de l'accomplir, mais vous devez faire attention à ne pas introduire d'autres problèmes avec des structures mal écrites.

Pour la plupart, mais je dirais que le GC fera un bon travail en gérant tout ce genre de choses pour vous.


0 commentaires

1
votes

Bien que le collecteur des ordures soit là, le mauvais code reste un mauvais code. Par conséquent, je dirais oui comme un développeur .NET que vous devriez toujours vous soucier du nombre d'objets que vous créez et de rédiger plus important encore le code optimisé.

J'ai vu une quantité considérable de projets être rejetés à cause de cette raison dans les critiques de code dans notre environnement et je crois fermement que c'est important.


0 commentaires

3
votes

J'ai vu trop de cas où les gens "optimisent" la merde de leur code sans trop de préoccupation pour la façon dont il est écrit ou à quel point cela fonctionne même. Je pense que la première pensée devrait aller vers la fabrication de code résoudre le problème de l'entreprise à la main. Le code doit être bien conçu et facilement maintenu et correctement testé.

Après tout cela, l'optimisation doit être envisagée, si le test indique qu'il est nécessaire.


0 commentaires

1
votes

.NET La gestion de la mémoire est très bonne et la possibilité de modifier programmable le GC si vous devez être bon.

J'aime le fait que vous puissiez créer vos propres méthodes sur les classes en héritant de l'idensposable et en le modifiant à vos besoins. C'est génial pour vous assurer que les connexions aux réseaux / fichiers / bases de données sont toujours nettoyées et ne fuient pas de cette façon. Il y a aussi l'inquiétude de nettoyer trop tôt.


0 commentaires

1
votes

Vous pouvez envisager d'écrire un ensemble de caches d'objet. Au lieu de créer de nouvelles instances, vous pouvez conserver une liste d'objets disponibles quelque part. Cela vous aiderait à éviter le GC.


0 commentaires

1
votes

Je suis d'accord avec tous les points indiqués ci-dessus: le collecteur des ordures est génial, mais il ne devrait pas être utilisé comme une béquille.

Je suis assis à travers de nombreuses heures gaspillées dans les critiques de codes débats sur des points plus fins du CLR. La meilleure réponse définitive est de développer une culture de performance dans votre organisation et de profiler activement votre application à l'aide d'un outil. Les goulots d'étranglement apparaîtront et vous adressez si nécessaire.


0 commentaires

3
votes

Conseil aléatoire:

Quelqu'un a mentionné de mettre des objets morts dans une file d'attente à être réutilisé au lieu de laisser le GC à eux ... mais faites attention, car cela signifie que le GC peut avoir plus de merde pour se déplacer lorsqu'elle consolide le tas, et peut ne pas réellement que T'aider. En outre, le GC utilise probablement déjà des techniques comme celle-ci. En outre, je connais au moins un projet où les ingénieurs ont essayé de mettre en commun et de faire mal à la performance. Il est difficile d'obtenir une intuition profonde sur le GC. Je recommanderais d'avoir un paramètre de pool et non non modifié afin que vous puissiez toujours mesurer les différences def entre les deux.

Une autre technique que vous pourriez utiliser dans C # est tombée en baisse de Native C ++ pour des pièces clés qui ne fonctionnent pas assez bien ... puis utilisez le Disposez le motif en C # ou C ++ / CLI pour des objets gérés qui contiennent des ressources non gérées.

En outre, assurez-vous que lorsque vous utilisez des types de valeur que vous n'utilisez pas de manière implicite les casser et les mettre sur le tas, ce qui pourrait être facile à faire de Java.

Enfin, assurez-vous de trouver un bon profileur de mémoire.


1 commentaires

+1 pour la suggestion intéressante selon laquelle la mise en commun peut réellement blesser la performance.



1
votes

Je pense que vous avez répondu à votre propre question - si cela devient un problème, alors oui! Je ne pense pas que ce soit une question .NET vs. Java, c'est vraiment un "Devrions-nous aller à des longueurs exceptionnelles pour éviter de faire certains types de travail" question. Si vous avez besoin de meilleures performances que vous avez, et après avoir fait du profil, vous trouvez que l'instanciation ou la collecte des ordures prend des tonnes de temps, c'est le moment d'essayer une approche inhabituelle (comme la mise en commun mentionnée).


0 commentaires

1
votes

J'aimerais être une "légende de logiciels" et je pouvais parler de cela à ma propre voix et à ma souffle, mais que je ne suis pas, je compte sur SL pour de telles choses.

Je suggère que le blog suivant publier par Andrew Hunter sur .NET GC serait utile:

http: // www.simple-Talk.com/dotnet/.net-Framework/unStanding-garge-collection-in-.net/


0 commentaires

1
votes

Même au-delà des aspects de performance, les Sémantics d'une méthode qui modifie un objet mutable transcédé sera souvent plus propre que celles d'une méthode qui renvoie un nouvel objet mutable basé sur une ancienne. Les déclarations: xxx

peuvent dans certains cas se comportent de manière identique, mais tandis que le premier fait une chose, ce dernier fera deux - équivalents à: xxx

si quelque chose est la seule référence, n'importe où dans l'univers, à un objet particulier, puis le remplace par une référence à un clone sera un non-op, et modifiera donc une dans l'objet équivaut à en retourner un nouveau. Si, toutefois, quelque chose identifie un objet auxquels d'autres références existent, la déclaration précédente modifierait l'objet identifié par toutes ces références, laissant toutes les références qui y sont attachées, tandis que ce dernier causerait Quelque chose devenir "détaché".

Selon le type de quelque chose et comment il est utilisé, sa pièce jointe ou son détachement peut être discuté de problèmes. La pièce jointe serait pertinente si un objet qui détient une référence à l'objet pourrait le modifier alors que d'autres références existent. La pièce jointe est discutable si l'objet ne sera jamais modifié, ou si aucune référence en dehors de quelque chose peut exister. Si on peut montrer que l'une des dernières conditions s'appliquera, remplacez-le, puis remplaçant quelque chose avec une référence à un nouvel objet ira bien. Sauf si le type de quelque chose n'est pas immuable, une telle démonstration nécessiterait une documentation au-delà de la déclaration de quelque chose , car .NET ne fournit aucun moyen standard d'annotant qu'une référence particulière Identifiez un objet qui, malgré son être de type mutable - personne n'est autorisé à modifier, ni d'annotant qu'une référence particulière devrait être le seul l'un n'importe où dans l'univers qui identifie un objet particulier. < / p>


1 commentaires

Merci de commenter, même si c'est 4 ans et demi plus tard :)