51 votes

Pourquoi une référence var locale provoque-t-elle une dégradation importante des performances?

Examiner le programme simple suivant:

using System;
using System.Diagnostics;

class Program
{
   private static void Main(string[] args)
   {
      const int size = 10000000;
      var array = new string[size];

      var str = new string('a', 100);
      var sw = Stopwatch.StartNew();
      for (int i = 0; i < size; i++)
      {
         var str2 = new string('a', 100);
         //array[i] = str2; // This is slow
         array[i] = str; // This is fast
      }
      sw.Stop();
      Console.WriteLine("Took " + sw.ElapsedMilliseconds + "ms.");
   }
}

Si je l'exécute, il est relativement rapide. Si je décommentez la "lenteur" de la ligne et de commenter le "rapide" de la ligne, c'est plus de 5 fois plus lent. Notez que dans les deux situations, il initialise la chaîne "str2" à l'intérieur de la boucle. Ce n'est pas optimisé à l'écart dans les deux cas (ce qui peut être vérifié en regardant l'IL ou du démontage).

Le code semble être en train de faire la même quantité de travail dans les deux cas. Il a besoin d'allouer/initialiser une chaîne de caractères, et ensuite attribuer une référence à un tableau de l'emplacement. La seule différence est de savoir si cette référence est le local var "str" ou "str2".

Pourquoi faut-il faire une telle grande différence de performance attribution de la mention "str" vs "str2"?

Si l'on regarde le démontage, il y a une différence:

(fast)
     var str2 = new string('a', 100);
0000008e  mov         r8d,64h 
00000094  mov         dx,61h 
00000098  xor         ecx,ecx 
0000009a  call        000000005E393928 
0000009f  mov         qword ptr [rsp+58h],rax 
000000a4  nop

(slow)
     var str2 = new string('a', 100);
00000085  mov         r8d,64h 
0000008b  mov         dx,61h 
0000008f  xor         ecx,ecx 
00000091  call        000000005E383838 
00000096  mov         qword ptr [rsp+58h],rax 
0000009b  mov         rax,qword ptr [rsp+58h] 
000000a0  mov         qword ptr [rsp+38h],rax

La "lenteur" de la version a deux "mov" opérations où le "rapide" version a "nop".

Quelqu'un peut-il expliquer ce qui se passe ici? Il est difficile de voir comment les deux extra mov opérations peuvent entraîner une >5x ralentissement, surtout depuis que j'attendrais la plupart du temps doit être dépensé dans la chaîne d'initialisation. Merci pour toutes les suggestions.

76voto

Daniel Points 7960

Vous avez raison que le code est d'environ la même quantité de travail dans les deux cas.

Mais le garbage collector finit par faire des choses très différentes dans les deux cas.

Dans l' str , à plus de deux instances de chaîne sont vivants à un moment donné. Cela signifie que (presque) tous les nouveaux objets dans la génération 0 à mourir, rien ne doit être promus à la génération 1. Depuis la génération 1 n'est pas en croissance, la GC n'a aucune raison de tenter cher "collections complètes".

Dans l' str2 version, toutes les nouvelles instances de chaîne sont vivants. Les objets à obtenir une promotion à la hausse des générations (qui peut impliquer le déplacement dans la mémoire). Aussi, depuis les générations subséquentes sont maintenant de plus en plus, le GC de temps en temps essayer d'exécuter des collections.

Notez que l' .NET GC tend à prendre le temps linéaire en le nombre d'objets en direct: live objets doit être traversés et déplacé hors de la voie, tandis que les objets morts ne coûte rien du tout (ils tout simplement être écrasé à la prochaine fois que la mémoire est allouée).

Cela signifie str est le meilleur des cas, pour le garbage collector de performance; tandis que, str2 est le pire des cas.

Jetez un oeil à la GC compteurs de performances pour votre programme, je pense que vous verrez des résultats très différents entre les programmes.

1voto

TomTom Points 35574

Non, une référence locale n'est pas lente.

Ce qui est lent, c’est la création de tonnes de nouvelles instances de chaînes, qui sont des classes. Tandis que la version rapide réutilise la même instance. Cela peut aussi être optimisé, alors que l'appel du constructeur ne peut pas.

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