2010-07-23 5 views
5

Je suis nouveau en C#, Parallel.ForEach, et .NET en général. Je veux paralléliser une recherche qui implique des milliers de lieux. Pour chaque emplacement, je calcule la distance du grand cercle. C'est un calcul que je veux étendre à différents cœurs. Ma question est comment puis-je faire cela si je n'ai que une variable thread-local, comme dans ce MSDN TPL example? Pour le résultat, j'ai regardé Interlocked, et vu ses options Add, CompareExchange, Decrement, Exchange, Increment et Read, mais je ne suis pas simplement ajouter, incrémenter, décrémenter, ou tester l'égalité. Je veux retourner l'objet, sur plusieurs threads fonctionnant en parallèle, qui a la distance globaleplus courte. Mon instinct dit que cela devrait être facile, que je devrais être en mesure de créer un petit objet qui enveloppe un Location et une distance, mais comment puis-je capturer la meilleure réponse de chaque fil et choisir la plus courte distance entre eux? Voici la version non-parallèle:Choisir le minimum parmi les minima en utilisant Parallel.ForEach

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    double closest = double.MaxValue; 
    Location closestLoc = null; 
    foreach (Location aLoc in allLocations) 
    { 
    if (aLoc != myLocation) 
    { 
     double d = greatCircle(myLocation, aLoc); 
     if (d < closest) 
     { 
     closest = d; 
     closestLoc = aLoc; 
     } 
    } 
    } 
    return closestLoc; 
} 

Je l'ai vu une DDJ Blog Post qui semblait offrir de bons conseils, mais je me suis demandé si elle était le meilleur conseil. Je vois l'auteur boucler sur des tableaux, et je me demande s'il n'y a pas une façon plus fonctionnelle de le faire. Dans le monde fonctionnel, j'utiliserais map, lambda et min.

Répondre

10

L'option la plus facile serait ici de passer à PLINQ:

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    return allLocations 
       .AsParallel() 
       .Min(location => greatCircle(myLocation, location)); 
} 

Cela étant dit, cela est fondamentalement juste aggregation with parallel constructs. Vous avez quelques options si vous voulez coller à la classe Parallel. Une option serait de synchroniser vous-même dans le bloc, en utilisant le verrouillage. Je ne recommanderais pas cela, car cela nuira à votre performance globale.

La meilleure solution consiste à utiliser les méthodes Parallel.ForEach qui fournissent l'état local. Ils vous permettent de réécrire ce que:

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    double closest = double.MaxValue; 
    Location closestLoc = null; 
    object sync = new object(); 

    Parallel.ForEach<Location, Tuple<double,Location>(
     allLocations, 
    () => new Tuple(double.MaxValue, null), 
     (location, loopState, localState) => 
     { 
      double d = greatCircle(myLocation, aLoc); 
      if (d < localState.Item1) 
       return new Tuple(d, aLoc); 
      else 
       return localState; 
     }, 
     localState => 
     { 
      lock(sync) 
      { 
       if (localState.Item1 < closest) 
       { 
        closest = localState.Item1; 
        closestLoc = localState.Item2; 
       } 
      } 
     } 
); 
    return closestLoc; 
} 

une couverture de protection à l'aide local state for aggregations in detail on my blog. Cela modifie fondamentalement l'opération à une opération de verrouillage par thread au lieu d'un verrou par élément de traitement, de sorte que vous obtenez un débit beaucoup plus élevé qu'une solution de verrouillage naïf.

+0

Wow, c'est à peu près aussi court et doux que possible. Cela fait chanter mon coeur fonctionnel. Merci! – gknauth

+0

@gknauth: Oui - J'ai ajouté l'option (correct/performant) Parallel.ForEach aussi. Je recommande d'apprendre ** comment ** l'autre option fonctionne, et essayer de le comprendre. C'est précieux, cependant, pour la plupart de ces opérations, j'utilise simplement PLINQ car c'est génial. –

+0

Merci de m'avoir donné des indices sur votre blog, c'est parfait. Ecrire un livre, je vais l'acheter! Ou si c'est trop de travail, je vais t'acheter le diner. – gknauth

Questions connexes