Подтвердить что ты не робот

Параллельный .ForEach медленнее, чем ForEach

Вот код:

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 мс. Что именно я делаю неправильно и почему это не работает, как я ожидаю?

4b9b3361

Ответ 1

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

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

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

Параллелизация не является бесплатной. Стоимость разделения работы между различными потоками должна быть крошечной по сравнению с объемом работы, выполняемой в потоке.

Дальнейшее чтение:

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

  1. Выделение делегатов.
  2. Звоню через них.