Parallel.Foreach как быстрый/медленный, как обычно. ForEach
ОБНОВЛЕНИЕ: я использовал Threading для разделения Loop в количестве ядер (8 в моем случае), и полный цикл прошел через менее 1 секунды. Таким образом, проблема не в том, что операция не быстрее с потоками. Почему в этом случае прорыв Parralel завершился неудачей?
Привет всем. Я хочу преобразовать свой ForEach с Parrallel.Foreach.
Проблема в том, что параллелизация практически не дает мне преимуществ.
Оригинал:
foreach (Entities.Buchung buchung in buchungen) {
Int32 categoryID = manager.GetCategoryID(new Regelengine.Booking(buchung)); // Average 4ms
buchung.Category = categoryID.ToString();
}
Parallel:
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 не сэкономит время.
Ответы
Ответ 1
Существует только один ресурс, с которым вы можете воспользоваться, используя Parallel.For: Процессные циклы. Когда у вас есть N ядер, вы можете теоретически ускорить ваш код с коэффициентом N. Однако требуется, чтобы на самом деле циклы процессора были ограничением в вашем коде. Это не так часто, если вы не выполняете дорогостоящий калькулятор. Другими ограничениями являются скорость жесткого диска, сетевое подключение, сервер базы данных, в отдельных случаях пропускная способность шины памяти. У вас есть только один из них, Parallel.For не может волшебным образом дать вам другой диск.
Тестирование того, будет ли Parallel.For ускорять ваш код, довольно просто. Просто запустите код без распараллеливания и понаблюдайте за загрузкой процессора в Taskmgr.exe или Perfmon. Если одно ядро не работает на 100%, тогда ваш код не вычисляется. Если он работает, скажем, на 10%, тогда вы можете только надеяться, что это займет 90% времени независимо от того, сколько ядер у вас есть. Который вы получите, перекрывая время ожидания ввода-вывода с временем обработки, два потока сделают это.
Ответ 2
Вопросы, которые вы должны учитывать в этом:
- Что такое накладные расходы на разворот потока?
- Что такое накладные расходы на безопасность потока (блокировки)?
- Где фактические узкие места и многопоточность действительно помогают?
Последнее ваше самое большое внимание здесь. Например, если вы максимизируете свой канал ввода-вывода, все потоки в мире не будут приседать. Итак, ваша задача связана с процессором или привязкой ввода/вывода?
Ответ 3
Я думаю, что ты прав, он выглядит слишком коротким, чтобы стоить использовать параллельный foreach. Я использую параллельный foreach только тогда, когда знаю, что в преддверии произойдет что-то важное, что потребует времени или может занять время, например, соединение с базой данных или если я отправляю много данных в веб-службу. Если он просто обрабатывает информацию на сервере, то, как только получение идентификаторов из коллекции, которая уже загружена в память, тогда это действительно не стоило бы.
Ответ 4
Parallelism не будет быстрее, если у вас нет доступных ядер. Поэтому, когда я вижу такой код, моя первая мысль заключается в том, что у вас есть другие потоки.
Это также может быть рабочая нагрузка. Логика синхронизации не является свободной, и каждая итерация не делает многого. Рассмотрите возможность просмотра других перегрузок Parallel.ForEach для параметров, которые вы можете настроить.
Также попробуйте использовать Parallel.For. Вы не можете читать из IEnumerable параллельно, но вы можете использовать IList, используя индексы.
Ответ 5
Во-первых, 1550 не так много. Например, сортировка массива из этих многих элементов обычно выполняется быстрее, когда выполняется последовательно, чем когда выполняется параллельно. Все зависит от операции.
Во-вторых, что делает GetCategoryID
? Использует ли он блокировки? В этом случае конструктор Regelengine.Booking
?
Общее время пробега 7 секунд указывает на то, что операция выполняется достаточно медленно, чтобы получить преимущество при распараллеливании. С другой стороны, ваш код, похоже, указывает на то, что на самом деле здесь не так много обработки. Вы, скорее всего, загружаете данные с диска или из базы данных. В обоих случаях это узкое место, с которым распараллеливание может (почти) ничего не против. Одновременная обработка ускоряет работу вашего кода только в том случае, если его вычисление ограничено.
Но вы не получили достаточной информации, чтобы определить это.