Question

D'après une SO réponse 1 à propos de Heap and Stack, cela m'a posé une question: pourquoi est-il important de savoir où les variables sont allouées?

Sur une autre réponse , quelqu'un a signalé que la pile était en cours plus rapide. Est-ce la seule implication? Quelqu'un pourrait-il donner un exemple de code dans lequel un simple changement d'emplacement d'allocation pourrait résoudre un problème (performances, par exemple)?

Notez que cette question est spécifique à .NET

1 la question est supprimée de SO.

Était-ce utile?

La solution

Tant que vous connaissez la sémantique, les seules conséquences de la pile contre la pile sont de s’assurer que vous ne la débordez pas et que vous devez savoir qu’il ya un coût à ramasser la pile dans le tas.

Par exemple, le JIT pourrait remarquer qu'un objet nouvellement créé n'a jamais été utilisé en dehors de la méthode actuelle (la référence ne pourrait jamais s'échapper ailleurs) et l'allouer sur la pile. Cela ne fait pas cela pour le moment, mais ce serait une chose légale à faire.

De même, le compilateur C # pourrait décider d'allouer toutes les variables locales sur le tas - la pile ne contiendrait qu'une référence à une instance de MyMethodLocalVariables et tous les accès aux variables seraient implémentés via celle-ci. (En fait, les variables capturées par les délégués ou les blocs d’itérateur ont déjà ce type de comportement.)

Votre question a été posée alors qu'Eric Lippert examinait C # en profondeur - j'ai une section expliquant ce qui se passe en C # 1, et il a estimé que les développeurs ne devraient pas s'en soucier .

Autres conseils

( modifier: Ma réponse d'origine contenait les simplifications excessives & "; les structures sont allouées sur la pile &"; confondues pile-tas-tas et valeur vs- référence concerne un peu, car ils sont couplés en C #. )

Que les objets vivent ou non sur la pile est un détail d'implémentation qui n'est pas très important. Jon a déjà bien expliqué cela. Lors du choix entre l'utilisation d'une classe et d'une structure, il est plus important de réaliser que les types de référence fonctionnent différemment des types de valeur. Prenez comme exemple la classe simple suivante:

public class Foo
{
   public int X = 0;
}

Considérons maintenant le code suivant:

Foo foo = new Foo();
Foo foo2 = foo;
foo2.X = 1;

Dans cet exemple, foo et foo2 sont des références au même objet. Le réglage de X sur foo2 affectera également foo1. Si nous modifions la classe Foo en une structure, ce n'est plus le cas . Cela est dû au fait que les structures ne sont pas accessibles via des références. L'affectation de foo2 en fera une copie.

Une des raisons pour mettre des éléments sur la pile est que le ramasse-miettes n'a pas à le nettoyer. Généralement, vous ne devriez pas vous inquiéter de telles choses. juste utiliser des cours! Les éboueurs modernes font un très bon travail. Certaines machines virtuelles modernes (telles que java 1.6) peuvent même déterminer s'il est prudent d'allouer des objets sur la pile même si ce ne sont pas des types valeur.

Je pense que la raison la plus simple est que si elle se trouve dans le tas, le garbage collection doit traiter la variable dès qu'elle n'est plus nécessaire. Lorsqu'elle est sur une pile, la variable est ignorée avec ce qui l'utilisait, telle qu'une méthode qui l'instanciait.

Dans .NET, il n’ya pas grand chose à discuter car ce n’est pas l’utilisateur du type qui décide où allouer les instances.

Les types de référence sont toujours alloués sur le tas. Les types de valeur sont alloués par défaut sur la pile. L'exception est si le type de valeur fait partie d'un type de référence, auquel cas il est alloué sur le tas avec le type de référence. C'est à dire. le concepteur d'un type prend cette décision au nom des utilisateurs.

Dans des langages tels que C ou C ++, l'utilisateur peut décider où les données sont allouées et dans certains cas particuliers, l'allocation depuis la pile peut être considérablement plus rapide que l'allocation depuis le tas.

Cela concerne la façon dont les allocations de tas sont gérées pour C / C ++. En fait, l’allocation de tas est assez rapide dans .NET (sauf quand cela déclenche une récupération de place), donc même si vous pouviez décider où allouer, j’imagine que la différence ne serait pas significative.

Cependant, étant donné que le tas est récupéré et que la pile ne l’est pas, il est évident que vous verriez des différences dans certains cas, mais cela n’est guère pertinent étant donné que vous n’avez pas vraiment le choix de .NET.

À mon avis, connaître les différences entre la pile et la pile et la façon dont les éléments sont alloués peut être très utile lorsque vous commencez vraiment à penser aux performances de votre application. Les questions suivantes rendent essentiel de comprendre les différences: Que pensez-vous est plus rapide et plus efficace pour .NET d'accéder? - pile ou tas. Dans quels scénarios .NET peut placer un type de valeur du tas?

Contrairement à la croyance populaire, il n’existe & # 8217; que la différence entre les piles et les tas dans un processus .NET. Les piles et les tas ne sont rien d'autre que des plages d'adresses dans la mémoire virtuelle et il n'y a aucun avantage inhérent dans la plage d'adresses réservée à la pile d'un thread particulier par rapport à la plage d'adresses réservée au segment de mémoire géré. L'accès à un emplacement de mémoire sur le segment de mémoire n'est ni plus rapide ni plus lent que l'accès à un emplacement de mémoire sur la pile. Plusieurs considérations peuvent, dans certains cas, corroborer l'affirmation selon laquelle l'accès à la mémoire des emplacements de pile est plus rapide. , dans l’ensemble, que l’accès mémoire aux emplacements de tas. Parmi eux:

  1. Sur la pile, la localité d'allocation temporelle (allocations rapprochées dans le temps) implique une localité spatiale (stockage proche dans l'espace). À son tour, lorsque la localité d’allocation temporelle implique la localité d’accès temporel (les objets alloués ensemble sont accédés ensemble), la mémoire de pile séquentielle tend à mieux fonctionner en ce qui concerne les caches de CPU et les systèmes de pagination du système d’exploitation.
  2. La densité de mémoire sur la pile a tendance à être plus élevée que sur le tas en raison de la surcharge du type de référence. Une densité de mémoire plus élevée entraîne souvent de meilleures performances, par exemple, car davantage d’objets tiennent dans le cache du processeur.
  3. Les piles de fils ont tendance à être assez petites & # 8211; La taille de pile maximale par défaut sous Windows est de 1 Mo et la plupart des threads ont tendance à n'utiliser que quelques pages de pile. Sur les systèmes modernes, les piles de tous les threads d'application peuvent être insérées dans le cache de la CPU, ce qui permet un accès extrêmement rapide aux objets de pile. (Par contre, des tas entiers s’intègrent rarement dans les caches de processeur.)
  

Cela dit, vous ne devriez pas déplacer toutes vos allocations vers le   empiler! Les piles de threads sur Windows sont limitées et il est facile de les épuiser   la pile en appliquant une récursion peu judicieuse et une grande pile   allocations.

J'ai beaucoup joué avec différents repères sur pile et tas et je conclurais en ce qui suit:

Performances similaires pour

  • petites applications (pas trop d'objets sur le tas)
  • objets de taille < 1 Ko

Meilleure performance de la pile Sligther pour (1x à 5 fois plus rapide)

  • Objets de taille comprise entre 1 Ko et 100 Ko

Des performances bien meilleures pour (jusqu'à 100 fois plus rapide ou même plus)

  • Grand nombre d'objets
  • Mémoire importante: beaucoup d'allocations par seconde et une mémoire complète
  • objets volumineux 10 Ko - 3 Mo (je suppose systèmes x64)
  • XBox (collecteur de déchets lent)

La meilleure approche est le regroupement de tableaux. C’est rapide comme pile, mais vous n’avez pas cette limitation comme avec pile.

Une autre implication de l'utilisation de stack est qu'il est thread-safe en desing.

La limite de mémoire par défaut pour la pile sous x64 Windows est de 4 Mo. Donc, vous ne risquez pas d’allouer plus de 3 Mo.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top