229 votes

Quels sont les dangers lorsqu’ils créent un thread avec une taille de la pile de 50 x par défaut ?

Je suis actuellement en train de travailler sur un très critique pour les performances du programme et d'un chemin j'ai décidé d'explorer qui peut aider à réduire la consommation de ressources a été d'augmenter ma threads' la taille de la pile, de sorte que je peux déplacer la plupart des données (float[]s) que je vais accès aux sur la pile (à l'aide d' stackalloc).

J'ai lu que par défaut, la taille de la pile d'un thread est de 1 MO, afin de afin de déplacer toutes mes float[]s I aurait pour étendre la pile d'environ 50 fois (à 50 MO~).

Je comprends ce qui est généralement considéré comme "dangereux" et n'est pas recommandé, mais après analyse comparative mon code actuel contre cette méthode, j'ai découvert un 530% d'augmentation de la vitesse de traitement! Donc je ne peut pas simplement passer par cette option, sans complément d'enquête, ce qui m'amène à ma question; quels sont les dangers associés à l'augmentation de la pile pour une telle grande taille (ce qui pourrait aller mal), et quelles précautions dois-je prendre pour minimiser de tels dangers?

Mon code de test,

public static unsafe void TestMethod1()
{
    float* samples = stackalloc float[12500000];

    for (var ii = 0; ii < 12500000; ii++)
    {
        samples[ii] = 32768;
    }
}

public static void TestMethod2()
{
    var samples = new float[12500000];

    for (var i = 0; i < 12500000; i++)
    {
        samples[i] = 32768;
    }
}

45voto

Vercas Points 3444

En comparant le code de test avec Sam, j'ai déterminé que nous sommes tous les deux raison!
Cependant, à propos de différentes choses:

  • Accès à la mémoire (lecture et écriture) est tout aussi rapide où que c'est - pile, global ou d'un segment.
  • L'allocation , cependant, est plus rapide sur la pile et le plus lent sur le tas.

Il va comme ceci: stack < global < heap. (allocation de temps)

Je vous conseille d'être prudent avec cela, cependant.
Je recommande ce qui suit:

  1. Lorsque vous avez besoin pour créer de nombreux tableaux souvent, à l'aide de la pile peut être une énorme amélioration.
  2. Si vous pouvez recycler un tableau, faites-le dès que vous le pouvez! Le tas est le meilleur endroit pour une longue durée de stockage des objets.

(Remarque: 1. s'applique uniquement aux types de valeur; les types de référence sera alloué sur le tas et la prestation sera réduite à 0)

Pour répondre à la question elle-même: je n'ai pas rencontré de problème avec n'importe quel grand-essai à la cheminée.
Je crois que le seul problème possible(s) peut venir de la pagination de la mémoire.
Si la pile doit être continu, vous pouvez obtenir de l' OutOfMemoryExceptions (ou StackOverflowExceptions?) parce que quel que soit le bloc doit être alloué à côté peuvent être prises. Si la pile est paginée, il devrait y avoir aucun problème à votre demande.

Cependant, n'importe où vous attribuer votre tableau, il besoin continu de la mémoire, de sorte que, à moins que la pile d'un thread peut être déplacé, c'est le plus enclin à l'erreur de localisation de l'affecter.

La section ci-dessous est ma première réponse. Il est conservé pour référence seulement.


Mon test indique la pile de la mémoire allouée et de la mémoire globale est au moins 15% plus lent qu' (120%) de segment de mémoire allouée pour l'utilisation dans des tableaux!

C'est mon code de test, et c'est un exemple de sortie:

Stack-allocated array time: 00:00:00.2224429
Globally-allocated array time: 00:00:00.2206767
Heap-allocated array time: 00:00:00.1842670
------------------------------------------
Fastest: Heap.

  |    S    |    G    |    H    |
--+---------+---------+---------+
S |    -    | 100.80 %| 120.72 %|
--+---------+---------+---------+
G |  99.21 %|    -    | 119.76 %|
--+---------+---------+---------+
H |  82.84 %|  83.50 %|    -    |
--+---------+---------+---------+
Rates are calculated by dividing the row's value to the column's.

J'ai testé sur Windows 8.1 Pro (avec mise à Jour 1), à l'aide d'un i7 4700 MQ, en vertu de l' .NET 4.5.1
J'ai testé les deux avec le x86 et le x64 et les résultats sont identiques.

Edit: j'ai augmenté la taille de la pile de tous les threads 201 MO, la taille de l'échantillon à 50 millions de dollars et une diminution des itérations à 5.
Les résultats sont les mêmes que ci-dessus:

Stack-allocated array time: 00:00:00.4504903
Globally-allocated array time: 00:00:00.4020328
Heap-allocated array time: 00:00:00.3439016
------------------------------------------
Fastest: Heap.

  |    S    |    G    |    H    |
--+---------+---------+---------+
S |    -    | 112.05 %| 130.99 %|
--+---------+---------+---------+
G |  89.24 %|    -    | 116.90 %|
--+---------+---------+---------+
H |  76.34 %|  85.54 %|    -    |
--+---------+---------+---------+
Rates are calculated by dividing the row's value to the column's.

Cependant, il semble que la pile est en fait obtenir plus lent.

28voto

Hans Passant Points 475940

J'ai découvert un 530% d'augmentation de la vitesse de traitement de l'!

C'est de loin le plus grand danger, je dirais. Il ya quelque chose de mal avec votre référence, le code qui se comporte de cette façon imprévisible généralement a un méchant bug caché quelque part.

Il est très, très difficile de consommer beaucoup d'espace de pile dans un .NET programme, des autres que par l'excès de la récursivité. La taille de la frame de pile de géré les méthodes sont définies dans la pierre. Simplement la somme des arguments de la méthode et les variables locales à une méthode. Moins ceux qui peuvent être stockées dans un PROCESSEUR inscrire, vous pouvez ignorer que depuis il y a si peu d'entre eux.

L'augmentation de la taille de la pile de ne pas accomplir quoi que ce soit, il vous suffit de réserver un tas d'espace d'adressage qui ne sera jamais utilisé. Il n'y a pas de mécanisme qui peuvent expliquer une augmentation de perf de ne pas utiliser la mémoire de stage.

C'est à la différence d'un natif du programme, en particulier celle qui est écrite en C, il peut aussi réserver de l'espace pour les tableaux sur le frame de pile. La base des logiciels malveillants vecteur d'attaque derrière les dépassements de la mémoire tampon de la pile. Possible en C#, vous devez utiliser l' stackalloc mot-clé. Si vous faites cela, alors le risque évident est d'avoir à écrire de code non sécurisé, qui est soumis à de telles attaques, ainsi que de l'aléatoire cadre de pile de corruption. Très difficile à diagnostiquer des bugs. Il y a une contre-mesure contre cette dans les, plus tard, de nervosité, je pense que départ au .NET 4.0, où la gigue génère du code pour mettre un "cookie" sur le frame de pile et vérifie si il est encore intact quand le retour de la méthode. Instant crash sur le bureau, sans aucun moyen d'intercepter ou de rapport de la mésaventure si cela arrive. C'est ... dangereux pour l'utilisateur de l'état mental.

Le thread principal de votre programme, celui qui a débuté par le système d'exploitation, aura un 1 MO de pile par défaut, 4 MO lorsque vous compilez votre programme de ciblage x64. En augmentant que nécessite l'exécution de Editbin.exe avec la PILE option dans un événement post-construction. En général, vous pouvez demander jusqu'à 500 MO avant de votre programme aura de la difficulté à obtenir commencé lors de l'exécution en mode 32 bits. Les fils peuvent aussi, beaucoup plus facile, bien sûr, la zone de danger, généralement plane autour de 90 MO pour un programme 32 bits. Déclenche lorsque le programme a été en cours d'exécution pendant un long temps et de l'espace d'adressage ai fragmenté de précédentes allocations. Total de l'adresse de l'utilisation de l'espace doit déjà être élevé, au cours d'un concert, d'obtenir ce mode de défaillance.

Triple-vérifiez votre code, il y a quelque chose de très mal. Vous ne pouvez pas obtenir un x5 speedup avec un plus gros stack, sauf si vous explicitement écrire votre code afin de prendre l'avantage. Ce qui nécessite toujours dangereux de code. En utilisant les pointeurs en C# a toujours un talent pour créer plus rapidement le code, il n'est pas soumis à des limites du tableau des contrôles.

22voto

Marc Gravell Points 482669

J’aurais une réserve là que j’ai simplement ne sais pas comment prédire - autorisations, GC (qui doit analyser la pile), etc. - tous pourraient être touchés. Je serais très tenté d’utiliser la mémoire non managée à la place :

8voto

PMF Points 3167

Une chose qui peut aller mal, c’est que vous ne pourriez pas obtenir l’autorisation de le faire. Sauf en mode de confiance totale, le cadre sera simplement ignorer la demande d’une plus grande taille de la pile (voir MSDN sur `` )

Au lieu d’augmenter la taille de la pile système à ces nombres énormes, je dirais de réécrire votre code afin qu’il utilise une implémentation de pile manuel et itération sur le tas.

6voto

MHOOS Points 1420

La haute performance des tableaux pourraient être accessibles de la même manière qu'un normal C# mais qui pourrait être le début des ennuis: Considérons le code suivant:

float[] someArray = new float[100]
someArray[200] = 10.0;

Vous vous attendez à une sortie de liés exception et ce tout à fait du sens parce que vous tentez d'accéder à l'élément 200, mais la valeur maximale autorisée est de 99. Si vous allez à la stackalloc route alors il n'y aura pas d'objet enroulé autour de votre tableau lié à vérifier et les suivants ne présentent aucune exception:

Float* pFloat =  stackalloc float[100];
fFloat[200]= 10.0;

Au-dessus de vous allouer assez de mémoire pour contenir 100 chars et que vous définissez la sizeof(float) emplacement de la mémoire qui commence à l'emplacement de ce mémoire + 200*sizeof(float) pour la tenue de votre float valeur de 10. Sans surprise, cette mémoire est en dehors de la mémoire allouée pour les chars, et personne ne sais ce qui pourrait être stockée à cette adresse. Si vous êtes chanceux, vous pourriez avoir utilisé certains actuellement inutilisée de la mémoire, mais en même temps, il est probable que vous risquez d'écraser certains endroit qui a été utilisé pour stocker d'autres variables. Pour Résumer: Imprévisible d'exécution de comportement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X