Parallel.Foreach так же быстро / медленно, как обычно ForEach - PullRequest
13 голосов
/ 04 февраля 2011

ОБНОВЛЕНИЕ: Я использовал Threading, чтобы разделить цикл на количество ядер (8 в моем случае), и полный цикл прошел менее чем за 1 секунду. Так что проблема не в том, что операция не быстрее с потоками. Почему в этом случае произошел сбой Parralel Extension?

Привет всем. Я хочу конвертировать мой ForEach с Parrallel.Foreach. Проблема в том, что парралелизация не приносит мне никакой пользы.

Оригинал:

foreach (Entities.Buchung buchung in buchungen) {
    Int32 categoryID = manager.GetCategoryID(new Regelengine.Booking(buchung)); // Average 4ms
    buchung.Category = categoryID.ToString();
}

Параллельное:

System.Threading.Tasks.Parallel.ForEach(buchungen, buchung => {
    Int32 categoryID = manager.GetCategoryID(new Regelengine.Booking(buchung));
    buchung.Category = categoryID.ToString();
});

Результаты:

---------------------------
Stopwatched Results for 1550 entries in the List:
---------------------------
Parallel.Foreach 00:00:07.6599066
Average Foreach: 00:00:07.9791303

Может быть, проблема в том, что фактическое действие в цикле настолько короткое? Но никто не может сказать мне, что распараллеливание 1550 операций на Intel I7 не сэкономит время.

Ответы [ 5 ]

9 голосов
/ 04 февраля 2011

Существует только один ресурс, которым вы можете воспользоваться, используя Parallel.For: циклы ЦП. Если у вас N ядер, то теоретически вы можете ускорить ваш код с коэффициентом N. Однако требуется, чтобы на самом деле именно циклы ЦП являлись ограничением в вашем коде. Что не часто случается, если вы не выполняете вычислительно дорогой код. Другими ограничениями являются скорость жесткого диска, сетевое соединение, сервер dbase, в некоторых случаях пропускная способность шины памяти. У вас есть только один из них, Parallel.For не может волшебным образом дать вам другой диск.

Проверить, сможет ли Parallel.For ускорить ваш код, довольно просто. Просто запустите код без распараллеливания и наблюдайте за загрузкой процессора в Taskmgr.exe или Perfmon. Если одно ядро ​​не работает на 100%, то ваш код не привязан к вычислениям. Если он работает, скажем, в 10%, то вы можете только надеяться, что это займет 90% времени, независимо от того, сколько у вас ядер. Что вы получите, перекрыв время ожидания ввода-вывода со временем обработки, и два потока выполнят это.

5 голосов
/ 04 февраля 2011

Вопросы, которые вы должны рассмотреть в этом:

  • Каковы затраты на раскручивание нити?
  • Каковы издержки моей безопасности потока (блокировки)?
  • Где реальные узкие места, и многопоточность действительно поможет?

Последнее ваше самое большое соображение здесь. Например, если вы максимизируете свой канал ввода / вывода, все потоки в мире не будут приседать. Итак, ваша задача связана с процессором или с I / O?

1 голос
/ 04 февраля 2011

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

Это также может быть рабочая нагрузка. Логика синхронизации не свободна, и каждая итерация мало что делает. Подумайте о том, чтобы посмотреть на другие перегрузки Parallel.ForEach, чтобы найти варианты, которые вы можете настроить.

Также попробуйте использовать Parallel.For. Вы не можете читать из IEnumerable в параллельном режиме, но вы можете из IList использовать индексы.

1 голос
/ 04 февраля 2011

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

0 голосов
/ 04 февраля 2011

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

Во-вторых, что делает GetCategoryID?Использует ли он замки?В связи с этим, конструктор Regelengine.Booking?

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

Но вы не дали достаточно информации, чтобы определить это.

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