7
votes

Dans le code géré, que dois-je m'occuper de garder de bonnes performances?

Je suis à l'origine un programmeur natif C ++, en C ++, chaque processus de votre programme est lié à votre code, c'est-à-dire que rien ne se passe à moins que vous ne le souhaitiez. Et chaque morceau de mémoire est alloué (et traité) selon ce que vous avez écrit. Donc, la performance est toute votre responsabilité, si vous faites du bien, vous obtenez une grande performance.

(Remarque: Ne vous plaignez pas du code que celui-ci ne s'est pas écrit tel que STL, c'est un code non géré de C ++ après tout, c'est la partie importante).

mais dans le code géré, tel que le code dans Java et C #, vous ne contrôlez pas tous les processus, et la mémoire est "cachée" ou non sous votre contrôle, dans une certaine mesure. Et cela rend la performance quelque chose de relativement inconnu, surtout que vous craignez de mauvaises performances.

Donc, ma question est la suivante: quelles problèmes et lignes audacieuses devrais-je m'occuper et garder à l'esprit pour obtenir une bonne performance dans le code géré?

Je ne pouvais penser que de certaines pratiques telles que:

  • Être conscient de la boxe et de la boîteboxing.
  • Choisir la collection correcte qui correspond le mieux à vos besoins et a le coût de fonctionnement le plus bas.

    Mais ceux-ci ne semblent jamais être suffisants et même convaincants! En fait, peut-être que je n'aurais pas dû les mentionner.

    Veuillez noter que je ne demande pas de code C ++ vs c # (ou Java) Comparaison, je viens de mentionner C ++ pour expliquer le problème.


0 commentaires

6 Réponses :


0
votes

Je suggérerais de mieux comprendre collection de déchets algorithmes. Vous pouvez trouver de bons livres à ce sujet, par ex. Le manuel de la collection de déchets (de Richard Jones, Antony Hosking, Eliot Moss).

Ensuite, votre question est pratiquement liée à la mise en œuvre particulière et peut-être même à une version spécifique de celle-ci. Par exemple, mono utilisé (par exemple dans la version 2.4) pour utiliser Collecteur de déchets de Boehm's utilise maintenant une copie générationnelle.

Et n'oubliez pas que certaines techniques de GC peuvent être remarquablement efficaces. Rappelez-vous le vieux papier d'A.Appel La collection de la poubelle peut être plus rapide que la répartition de la pile (Mais aujourd'hui, la performance du cache importe beaucoup plus, les détails sont différents).

Je pense que cela est conscient de la boxe (& Unboxing) et de l'attribution suffit. Certains compilateurs sont capables d'optimiser ces éléments (en évitant certains d'entre eux).

N'oubliez pas que la performance GC peut varier considérablement. Il y a de bonnes gcs (pour votre application) et de mauvaises.

Et certaines implémentations de GC sont assez rapides. Par exemple, celui à l'intérieur OCAML

Je ne dérangerais pas que beaucoup: l'optimisation prématurée est un mal.

(et la gestion de la mémoire C ++, même avec des pointeurs intelligents, ou avec des compteurs REF, peuvent souvent être considérés comme une technique de collecte des ordures pauvre de l'homme; et vous n'avez pas le contrôle total sur ce que C ++ fait-ond Implémentez votre :: Opérateur Nouveau Utilisation des appels de système spécifiques du système d'exploitation -, de sorte que vous ne connaissez pas vraiment une priori sa performance)


0 commentaires

1
votes

La principale chose à garder à l'esprit avec la performance avec les langues gérées est que votre code peut modifier la structure au moment de l'exécution pour être mieux optimisé.

Par exemple, par défaut JVM La plupart des gens utilisent est Sun's hotspot vm qui optimisera votre code comme il s'exécute en convertissant des parties du programme en code natif, dans la doublure de la mouche et autres optimisations (telles que le CLR ou d'autres roulements gérés) que vous n'obtiendrez jamais C ++. De plus, Hotspot détectera également quelles parties de votre code sont utilisées et optimisent en conséquence. Ainsi, comme vous pouvez le constater que l'optimisation des performances sur un système géré est légèrement plus difficile que sur un système non géré, car vous avez une couche intermédiaire pouvant rendre le code plus rapide sans votre intervention.

Je vais invoquer la loi de Optimisation prématurée ici et dites que vous devriez d'abord créer la solution correcte alors, si la performance devient un problème, revenez et mesurez ce qui est effectivement lent avant de tenter d'optimiser.


3 commentaires

.NET TOUJOURS JIT compile tout au code natif. Et je ne pense pas que cela utilise l'affranchissement spéculatif ou la recompilation.


@Benvoigt Selon Cette réponse le .NET JIT Le compilateur effectue un certain nombre d'optimisations, y compris la méthode dans la doublure. Le point que je faisait est que les langues gérées offrent la possibilité d'effectuer de telles optimisations au moment de l'exécution, de manière appropriée ou utilisée.


Il est enlimage lorsque la cible peut être déterminée de manière statique. L'affranchissement spéculatif est une technique beaucoup plus avancée que STROPOT fait et .NET ne le fait pas.



4
votes

réutiliser de gros objets est très important dans mon expérience.

Les objets sur le gros tas de l'objet sont implicitement génération 2 et nécessitent ainsi un GC complet pour nettoyer. Et c'est cher.


2 commentaires

Je suppose que cela est vrai pour certaines implémentations (peut-être la plupart); Mais c'est un détail de mise en œuvre, bien qu'il soit important.


@BasileStarynkevitch c'est très vrai. La mise en commun de l'objet pour un GC non générant pourrait en fait dégrader performance!



5
votes

Il n'y a pas de réponse unique ici. Le seul moyen de répondre à cela est: profil. Mesurer tôt et souvent. Les goulots d'étranglement ne sont généralement pas là où vous vous attendez. Optimiser les choses qui réellement blessées. Nous utilisons MVC-mini-profileur pour cela, mais tout outil similaire fonctionnera.

Vous semblez vous concentrer sur GC; Maintenant, que peut parfois être être un problème, mais généralement uniquement dans des cas spécifiques; Pour la majorité des systèmes, le GC générationnel fonctionne bien.

évidemment des ressources externes seront lentes; La mise en cache peut être critique: dans des scénarios étranges avec des données très longues, vous trouverez des astuces que vous pouvez faire avec des structures pour éviter les collecteurs de longue-2; La sérialisation (fichiers, réseau, etc.), matérialisation (ORM) ou juste mauvaise collecte / choix d'algorithn peut être le plus gros problème - vous ne peut pas savoir jusqu'à ce que vous ne mesuriez.


Deux choses cependant:

  • Assurez-vous de comprendre ce qu'est Idisposable et "en utilisant" signifie
  • Ne concatéez pas de chaînes dans des boucles; La concaténation massive est le travail de StringBuilder

0 commentaires

0
votes

.NET Les génériques ne sont pas spécialisés sur les types de référence, ce qui limite considérablement la quantité d'inlosication. Il peut (dans certains points chauds de performance), il est logique de renoncer à un type de conteneur générique en faveur d'une implémentation spécifique qui sera mieux optimisée. (Remarque: cela ne signifie pas utiliser les conteneurs .NET 1.X avec type d'élément objet ).


0 commentaires

-1
votes

Vous devez: L'utilisation de gros objets est très importante dans mon expérience.

Les objets sur le gros tas de l'objet sont implicitement génération 2 et nécessitent ainsi un GC complet pour nettoyer. Et c'est cher.


0 commentaires