Существует несколько недоразумений относительно того, как работает Parallel.XYZ
.
В комментариях упоминалось несколько замечательных моментов и предложений, поэтому я не буду их повторять. Скорее, я хотел бы поделиться некоторыми мыслями о параллельном программировании.
Параллельный класс
Когда мы говорим о параллельном программировании, мы обычно различаем два вида: Параллелизм данных и Параллелизм задач . Первый выполняет одну и ту же функцию (и) над блоком данных параллельно. Последний выполняет несколько независимых функций параллельно.
(Существует также 3-я модель, называемая конвейером, которая представляет собой смесь этих двух. Я не буду тратить на нее время, если она вам интересна. предложит выполнить поиск потока данных параллельной библиотеки задач или System.Threading.Channels .)
Класс Parallel поддерживает обе модели. For
и ForEach
предназначены для параллелизма данных, а Invoke
- для параллелизма задач.
Разделение
В случае параллелизма данных сложная часть состоит в том, как вы нарезаете данные чтобы получить лучшую пропускную способность / производительность. Вы должны учитывать размер сбора данных, структуру данных, логики обработки c и доступные ядра (а также многие другие аспекты). Так что не существует единого правила для всех.
Основная забота о разделении состоит в том, чтобы не использовать ресурсы недостаточно (некоторые ядра простаивают, а другие усердно работают) и не использовать чрезмерное использование ( ожидающих заданий намного больше, чем доступных ядер, поэтому накладные расходы на синхронизацию могут быть значительными).
Предположим, ваш лог c обработки устойчив (другими словами, различные входные данные не будут существенно изменять время обработки ). В этом случае вы можете балансировать нагрузку данных между исполнителями. Если исполнитель завершает свою работу, он может захватить новый фрагмент данных для обработки.
Способ, которым вы выбираете, какие данные должны go, для какого исполнителя может быть определено Partitioner
( 1 ). По умолчанию. NET поддерживает Range, Chunk, Ha sh и чередование. Некоторые из них являются статическими c (разбиение выполняется перед любой обработкой), а некоторые из них динамическими c (в зависимости от скорости обработки один исполнитель может получать больше, чем другие).
Следующие два превосходных статьи могут дать вам лучшее представление о том, как работает каждое из разделов:
Безопасность потоков
Если каждый исполнитель может выполнять свою задачу обработки без необходимости взаимодействия с другими, то они считаются независимыми. Если вы можете разработать свой алгоритм с независимыми процессорами, тогда вы минимизируете синхронизацию.
В случае For
и ForEach
каждый раздел может иметь собственное локальное хранилище раздела. Это означает, что вычисления независимы, поскольку промежуточные результаты хранятся в хранилище с поддержкой разделов. Но, как обычно, вы хотите объединить их в одну коллекцию или даже в значение.
Вот почему эти Parallel
методы имеют параметры body
и localFinally
. Первый используется для определения отдельной обработки, а второй - функции агрегирования и слияния. (Это похоже на подход Map-Reduce). В последнем случае вы знаете о потокобезопасности самостоятельно.
PLINQ
Я не хочу исследовать это топи c, которые выходят за рамки вопроса. Но я хотел бы указать вам, с чего начать:
Полезные ресурсы
РЕДАКТИРОВАТЬ: Как решить, что стоит запускать параллельно?
Не существует единой формулы (по крайней мере, насколько мне известно), которая сообщила бы вам, когда имеет смысл использовать параллельное выполнение. Как я попытался выделить в разделе «Разбиение на разделы», это довольно сложный объект c, поэтому для поиска оптимального решения необходимо несколько экспериментов и тонкой настройки.
Я настоятельно рекомендую вам измерить и попробовать несколько различных настроек .
Вот мой совет, как вам с этим справиться:
- Попытайтесь понять текущие характеристики вашего приложения
- Выполните несколько различных измерений, чтобы определить узкое место выполнения
- Захватить текущие показатели производительности решения в качестве базового уровня
- Если возможно, попробуйте извлечь этот фрагмент кода из базы кода, чтобы облегчить тонкую настройку
- Попытайтесь решить та же проблема с несколькими различными аспектами и с различными входными данными
- Измерьте их и сравните с вашими базовыми показателями
- Если вы удовлетворены результатом, поместите этот фрагмент кода в свою базу кода и измерьте опять же при других нагрузках
- Попробуйте захватить как можно больше релевантных
- Если возможно, рассмотрите возможность выполнения обоих (последовательных и параллельных) решений и сравните их результаты.
- Если вас устраивает, то избавьтесь от последовательного кода
Подробности
- Есть несколько действительно хороших инструментов, которые могут помочь вам получить представление о вашем приложении. Для. NET Профилирование Я бы посоветовал вам попробовать CodeTrack . Визуализатор параллелизма также является хорошим инструментом, если не нужны специальные метрики.
- Под несколькими измерениями я имел в виду, что вы должны измерять несколько раз с помощью нескольких различных инструментов, чтобы исключить особые обстоятельства. Если измерить только один раз, можно получить ложноположительный результат. Итак, дважды отмерьте, один раз отрежьте.
- Ваша последовательная обработка должна служить базой. Избыточное распараллеливание базы может вызвать определенные накладные расходы, поэтому имеет смысл сравнить ваше новое решение Shine с текущим. Недостаточное использование также может привести к значительному снижению производительности.
- Если вы можете извлечь код проблемы, c, вы можете выполнить микротест. Я рекомендую вам взглянуть на замечательный инструмент Benckmark. NET для создания тестов.
- Эту проблему можно решить многими способами. Поэтому попробуйте найти несколько разных подходов (например, Parallel , PLINQ можно более или менее использовать для тех же задач)
- Как я уже говорил ранее, измеряйте, измеряйте и измеряйте . Вы также должны иметь в виду. NET постарайтесь быть умным. То, что я имею в виду, например,
AsParallel
не дает вам гарантии, что он будет работать параллельно . . NET проанализируйте ваше решение и структуру данных и решите, как его запустить. С другой стороны, вы можете принудительно выполнить параллельное выполнение , если уверены, что это поможет.
Существуют библиотеки, такие как
Scientist. NET, которые могут помочь вам выполнить этот короткий параллельный запуск и процесс сравнения. Наслаждайтесь: D