2010-10-20 11 views
3

Je travaille sur certaines applications qui nécessitent une latence très faible et poussent beaucoup de mémoire et effectuent des tests sur la façon dont par exemple. allouer une liste ad hoc vs pré-allouer et effacer une liste effectue. Je m'attendais à ce que les tests de pré-allocation de la mémoire fonctionnent beaucoup plus vite mais à ma grande surprise ils sont en fait légèrement plus lents (quand je laisse le test tourner pendant 10 minutes, la différence moyenne est d'environ 400ms)..NET préallocation de la mémoire par rapport à l'allocation ad-hoc

Voici le code de test que j'ai utilisé:

class Program 
{ 
    private static byte[] buffer = new byte[50]; 
    private static List<byte[]> preAlloctedList = new List<byte[]>(500); 

    static void Main(string[] args) 
    { 
     for (int k = 0; k < 5; k++) 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 

      for (int i = 0; i < 1000000; i++) 
      { 
       List<byte[]> list = new List<byte[]>(300); 

       for (int j = 0; j < 300; j++) 
       { 
        list.Add(buffer); 
       } 
      } 

      sw.Stop(); 
      Console.WriteLine("#1: " + sw.Elapsed); 
      sw.Reset(); 
      sw.Start(); 

      for (int i = 0; i < 1000000; i++) 
      { 
       for (int j = 0; j < 300; j++) 
       { 
        preAlloctedList.Add(buffer); 
       } 

       preAlloctedList.Clear(); 
      } 
      sw.Stop(); 
      Console.WriteLine("#2: " + sw.Elapsed); 
     } 

     Console.ReadLine(); 
    } 
} 

Maintenant, ce qui est vraiment intéressant, je courais côté perfmon côte à côte et vu le schéma suivant qui ressemble à je m'y attendais:

Vert = Gen 0 collections
Bleu = Numéroté Octets/sec
Rouge =% Temps en GC

L'application de la console ci-dessous montre les runtimes de test pour 1 et # 2
alt text

Alors, ma question est: pourquoi le test # 1 est-il plus rapide que # 2? De toute évidence, je préférerais avoir les statistiques de perfmon du test n ° 2 dans mon application car il n'y a pratiquement pas de pression de mémoire, pas de collections GC, etC# 1 semble être légèrement plus rapide?
List.Clear() porte-t-il autant de frais généraux?

Merci,

Tom

EDIT Je l'ai fait un autre test, avec la même configuration, mais l'exécution de l'application avec le serveur GC activé, maintenant # 2 devient un peu plus rapide alt text

Répondre

4

I suspecter la raison pour laquelle le test n ° 1 est plus rapide est que la collecte des ordures se passe sur un thread séparé, et le surcoût d'allocation est inférieur à l'appel List<T>.Clear supplémentaire. Comme aucune de ces listes n'est grande (seulement 300 références chacune), et qu'elles sont toutes créées et non racinées dans une boucle serrée, elles resteront généralement dans la génération 0.

Je l'ai remarqué lors du profilage dans le passé - la réutilisation d'un List<T> et l'appel de Clear sont souvent plus lents que la simple réaffectation. Clear() efface réellement le tableau interne ainsi que réinitialise les paramètres de la liste, qui je crois a (légèrement) plus de frais généraux que l'allocation initiale de la liste.

Cependant, cet exemple, à mon avis, montre vraiment que le GC dans .NET est très, très efficace.

+0

Merci, pour votre réponse, cela a du sens. Quelle option choisiriez-vous dans un scénario de production très similaire? Maintenant, ce qui est très intéressant, quand je lance la même application avec Server GC activé, # 2 devient en fait plus rapide. J'ai mis à jour le poste et joint une capture d'écran – TJF

+0

@Tom: Le mode serveur rend le débit global plus rapide au prix d'une latence plus élevée. Le mieux est à vous. Cela étant dit, je n'utilise généralement que # 1 dans mon environnement de production - le code est plus propre, et il a tendance à mieux fonctionner globalement. –

3

List.Clear() porte-t-il autant de temps système?

Oui, par rapport à un (unique) GC.Collect(0), faisant quelques milliers d'appels à Clear() pourrait très bien être plus lent.

Pour ce que j'ai appris, le système de mémoire dotNet est très rapide dans l'allocation/désallocation des blocs de mémoire à court terme.

Mais attention à porter des conclusions de ce test simple sur votre application réelle.

+0

J'ai regardé l'implémentation de la liste et Clear() effectue un appel externe. Savez-vous ce que cela signifie? Je pourrais faire une implémentation List avec Clear() ne réinitialisant que le champ de taille – TJF

+0

nevermind, c'est ce que Clear fait: Définit une plage d'éléments dans le tableau à zéro, à false, ou à null, selon le type d'élément. – TJF

+0

@Tom: Vous devez effacer le tableau, sinon vous allez laisser tous les éléments enracinés, donc le GC ne peut pas les collecter. En général, j'essayerais d'éviter de réinventer la roue comme ça ... –