Parallel.ForEach Медленнее, чем ForEach - PullRequest
28 голосов
/ 17 мая 2011

Вот код:

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);
}

Проблема в том, что Parallel.ForEach занимает около 400 мс против обычного foreach, что занимает около 40 мс.Что именно я делаю не так и почему не работает так, как я ожидаю?

Ответы [ 5 ]

149 голосов
/ 18 мая 2011

Предположим, у вас есть задача для выполнения.Допустим, вы учитель математики, и у вас есть двадцать работ для оценки.На оценку работы у вас уходит две минуты, так что у вас уходит около сорока минут.

Теперь давайте предположим, что вы решили нанять несколько помощников, которые помогут вам оценивать документы.Вам понадобится час, чтобы найти четырех помощников.Каждый из вас берёт по четыре бумаги и все готово за восемь минут.Вы продали 40 минут работы за 68 минут работы, включая дополнительный час на поиск помощников, так что это не экономия.Затраты на поиск помощников превышают затраты на выполнение работы самостоятельно.

Теперь предположим, что у вас есть двадцать тысяч работ для оценки, так что это займет у вас около 40000 минут.Теперь, если вы потратите час на поиск помощников, это победа.Каждый из вас берет 4000 работ и уходит в общей сложности 8060 минут вместо 40000 минут, экономия почти в 5 раз. Затраты на поиск помощников в основном не имеют значения.бесплатно . Стоимость разделения работы между различными потоками должна быть крошечной по сравнению с объемом работы, проделанной за поток.

Дополнительная информация:

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

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

9 голосов
/ 17 мая 2011

Дополнительные затраты на создание всех потоков для вашего перечислимого VS, просто выполняя нумерацию, скорее всего, причина замедления. Parallel.ForEach не является общим движением, увеличивающим производительность; необходимо взвесить, может ли операция, которая должна быть выполнена для каждого элемента, блокироваться.

Например, если вы хотите сделать веб-запрос или что-то вместо простой записи в консоль, параллельная версия может быть быстрее. На самом деле, простая запись в консоль является очень быстрой операцией, поэтому затраты на создание потоков и их запуск будут медленнее.

9 голосов
/ 17 мая 2011

Первое, что вы должны понять, это то, что не весь параллелизм полезен. В параллелизме есть издержки, и эти издержки могут быть, а могут и не быть значительными, в зависимости от сложности параллелизации. Поскольку работа в вашей параллельной функции очень мала, накладные расходы на управление, которые должен выполнять параллелизм, становятся значительными, что замедляет общую работу.

4 голосов
/ 26 февраля 2013

Как сказал предыдущий писатель, с Parallel.ForEach связаны некоторые накладные расходы, но это не то, почему вы не видите улучшения своей производительности. Console.WriteLine - синхронная операция, поэтому одновременно работает только один поток. Попробуйте изменить тело на что-нибудь неблокирующее, и вы увидите увеличение производительности (если объем работы в теле достаточно велик, чтобы перевесить накладные расходы).

0 голосов
/ 11 декабря 2018

Мне нравится ответ salomons и я хотел бы добавить, что у вас также есть дополнительные накладные расходы

  1. Распределение делегатов.
  2. Звоню через них.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...