web-dev-qa-db-fra.com

Parallel.ForEach plus lent que ForEach

Voici le code:

using (var context = new AventureWorksDataContext())
{
    IEnumerable<Customer> _customerQuery = from c in context.Customers
                                           where c.FirstName.StartsWith("A")
                                           select c;

    var watch = new Stopwatch();
    watch.Start();

    var result = Parallel.ForEach(_customerQuery, c => Console.WriteLine(c.FirstName));

    watch.Stop();
    Debug.WriteLine(watch.ElapsedMilliseconds);

    watch = new Stopwatch();
    watch.Start();

    foreach (var customer in _customerQuery)
    {
        Console.WriteLine(customer.FirstName);
    }

    watch.Stop();
    Debug.WriteLine(watch.ElapsedMilliseconds);
}

Le problème est que Parallel.ForEach prend environ 400 ms par rapport à une foreach régulière, ce qui prend environ 40 ms. Qu'est-ce que je fais de travers et pourquoi cela ne fonctionne-t-il pas comme prévu?

28
Mike Diaz

Supposons que vous ayez une tâche à accomplir. Disons que vous êtes un professeur de mathématiques et que vous avez vingt papiers à noter. Il vous faut deux minutes pour classer un document, il vous faudra donc environ quarante minutes.

Supposons maintenant que vous décidiez d'engager des assistants pour vous aider à évaluer les papiers. Il vous faut une heure pour localiser quatre assistants. Vous prenez chacun quatre papiers et vous avez tous terminé en huit minutes. Vous avez échangé 40 minutes de travail contre 68 minutes de travail au total, y compris l’heure supplémentaire nécessaire pour trouver les assistants. Il ne s’agit donc pas d’une économie. Les frais généraux liés à la recherche d’assistants sont plus importants que le coût de la réalisation du travail vous-même.

Supposons maintenant que vous ayez vingt mille papiers à noter et que cela vous prenne environ 40000 minutes. Maintenant, si vous passez une heure à trouver des assistants, c'est une victoire. Vous prenez chacun 4000 papiers et vous réalisez un total de 8060 minutes au lieu de 40000 minutes, soit une économie de presque un facteur de 5. Les frais généraux liés à la recherche des assistants sont fondamentalement hors de propos.

La parallélisation est pas libre. Le coût de la division du travail entre différents threads doit être minime comparé à la quantité de travail effectué par thread.

Lectures complémentaires:

https://en.wikipedia.org/wiki/Amdahl%27s_law

https://en.wikipedia.org/wiki/Gustafson%27s_law

142
Eric Lippert

La première chose à réaliser est que tout parallélisme n’est pas bénéfique. Le parallélisme entraîne une surcharge, qui peut être importante ou non, en fonction de la complexité de la parallélisation. Dans la mesure où le travail dans votre fonction parallèle est très petit, la gestion du parallélisme devient trop lourde, ce qui ralentit le travail dans son ensemble.

9
skaz

La surcharge supplémentaire liée à la création de tous les threads pour votre VS énumérable qui vient d’exécuter la variable numérique est plus que probablement la cause du ralentissement. Parallel.ForEach n'est pas un coup qui augmente la performance; il faut déterminer si l'opération à exécuter pour chaque élément est susceptible de bloquer.

Par exemple, si vous deviez faire une requête Web ou quelque chose de différent au lieu d'écrire simplement sur la console, la version parallèle pourrait être plus rapide. Telle qu’elle est, écrire dans la console est une opération très rapide, ce qui ralentit la création des threads et leur démarrage.

9
Tejs

Comme le rédacteur précédent l’a dit, il existe des frais généraux associés à Parallel.ForEach, mais ce n’est pas pour cette raison que vous ne pouvez pas constater d’amélioration de vos performances. Console.WriteLine est une opération synchrone, de sorte qu'un seul thread fonctionne à la fois. Essayez de changer le corps pour quelque chose de non bloquant et vous verrez la performance augmenter (tant que la quantité de travail dans le corps est assez grande pour surpasser les frais généraux).

4
salomons

J'aime salomons répondre et voudrais ajouter que vous avez également des frais généraux supplémentaires de

  1. Allouer des délégués.
  2. Appeler à travers eux.
0
Yola