2010-02-06 8 views
1

Je profile une application C# et il ressemble à deux threads appelant chacun Dictionary<>.ContainsKey() 5000 fois chacun sur deux dictionnaires distincts mais identiques (avec seulement deux éléments) est deux fois plus lent qu'un thread appelant Dictionary<>.ContainsKey() sur un seul dictionnaire 10000 fois.Plusieurs threads ralentissent l'accès global au dictionnaire?

Je mesure le "temps de filetage" en utilisant un outil appelé JetBrains dotTrace. J'utilise explicitement des copies des mêmes données, donc il n'y a pas de primitives de synchronisation que j'utilise. Est-il possible que .NET fasse de la synchronisation dans les coulisses?

J'ai une machine dual core, et il y a trois threads en cours d'exécution: l'un est bloqué en utilisant Semaphore.WaitAll() tandis que le travail est effectué sur deux nouveaux threads dont la priorité est ThreadPriority.Highest.

Des coupables évidents comme, ne pas réellement exécuter le code en parallèle, et ne pas utiliser une version release ont été exclus.

EDIT:

Les gens veulent le code. Très bien, alors:

private int ReduceArrayIteration(VM vm, HeronValue[] input, int begin, int cnt) 
    { 
     if (cnt <= 1) 
      return cnt; 

     int cur = begin; 

     for (int i=0; i < cnt - 1; i += 2) 
     { 
      // The next two calls are effectively dominated by a call 
      // to dictionary ContainsKey 
      vm.SetVar(a, input[begin + i]); 
      vm.SetVar(b, input[begin + i + 1]); 
      input[cur++] = vm.Eval(expr); 
     } 

     if (cnt % 2 == 1) 
     { 
      input[cur++] = input[begin + cnt - 1]; 
     } 

     int r = cur - begin; 
     Debug.Assert(r >= 1); 
     Debug.Assert(r < cnt); 
     return r; 
    } 

    // From VM 
    public void SetVar(string s, HeronValue o) 
    { 
     Debug.Assert(o != null); 
     frames.Peek().SetVar(s, o); 
    } 

    // From Frame 
    public bool SetVar(string s, HeronValue o) 
    { 
     for (int i = scopes.Count; i > 0; --i) 
     { 
      // Scope is a derived class of Dictionary 
      Scope tbl = scopes[i - 1]; 
      if (tbl.HasName(s)) 
      { 
       tbl[s] = o; 
       return false; 
      } 
     } 
     return false; 
    } 

Maintenant, voici le code de reproduction de fil, ce qui pourrait être retardé:

public static class WorkSplitter 
{ 
    static WaitHandle[] signals; 

    public static void ThreadStarter(Object o) 
    { 
     Task task = o as Task; 
     task.Run(); 
    } 

    public static void SplitWork(List<Task> tasks) 
    { 
     signals = new WaitHandle[tasks.Count]; 
     for (int i = 0; i < tasks.Count; ++i) 
      signals[i] = tasks[i].done; 
     for (int i = 0; i < tasks.Count; ++i) 
     { 
      Thread t = new Thread(ThreadStarter); 
      t.Priority = ThreadPriority.Highest; 
      t.Start(tasks[i]); 
     } 
     Semaphore.WaitAll(signals); 
    }   
} 
+0

Pourriez-vous peut-être expliquer le problème que vous essayez de résoudre avec ces threads et dictionnaires ... –

+0

Écrire un interpréteur, et j'ai besoin de paralléliser certaines opérations vectorielles. – cdiggins

+0

Vous devriez publier votre code. – ChaosPandion

Répondre

4

Même s'il y avait un blocage dans le dictionnaire (il n'y a pas), il ne pourrait affecter votre mesures puisque chaque thread en utilise un séparé. L'exécution de ce test 10 000 fois n'est pas suffisante pour obtenir des données de synchronisation fiables, ContainsKey() ne prend que 20 nanosecondes environ. Vous aurez besoin d'au moins plusieurs millions de fois pour éviter la planification d'artefacts.

+0

Le problème est que j'ai besoin que ce soit rapide sur 10.000 itérations. Obtenir des horaires sur plus d'itérations, serait l'optimisation pour un cas que je ne me soucie pas. Je suis juste un diable d'un temps en essayant d'utiliser pleinement le deuxième noyau pour les opérations que je sais théoriquement devrait être parallélisable. – cdiggins

+2

Je ne vois pas comment vous pourriez vous attendre à un fonctionnement parallèle lorsque le thread ne s'exécute que pendant 200 microsecondes. Vous auriez de la chance d'avoir les deux threads démarrés exactement en même temps. –

+0

Le thread fonctionne beaucoup plus longtemps que cela, l'ensemble du test prend environ 100 msec à courir dans le temps sans instrumentation. Mon profileur dit que le deuxième point chaud le plus chaud est "FindEntry" qui est à l'intérieur du dictionnaire. Il dit 296 msec est passé ici dans le test avec un thread, et 577 msec est passé dans le test avec deux threads. Ceci est vraiment cohérent dans des dizaines d'essais, et souffle mon esprit. – cdiggins

Questions connexes