Параллельное программирование и C ++ - PullRequest
8 голосов
/ 01 ноября 2008

В последнее время я много писал о параллельных вычислениях и программировании, и я заметил, что существует множество шаблонов, которые возникают, когда речь идет о параллельных вычислениях. Отмечая, что Microsoft уже выпустила библиотеку вместе с техническим выпуском сообщества Microsoft Visual C ++ 2010 (называется Библиотека параллельных шаблонов), мне интересно, какие распространенные шаблоны параллельного программирования вы использовали и с которыми сталкиваетесь, о которых, возможно, стоит помнить? Есть ли у вас какие-то идиомы, которым вы следуете, и шаблоны, которые, как вам кажется, продолжают появляться при написании параллельных программ на C ++?

Ответы [ 4 ]

17 голосов
/ 01 ноября 2008

Модели:

  • Produce / Потребитель

    • Один поток производит данные
    • Один поток потребляет данные
  • Петлевой параллелизм

    • Если вы можете показать, что каждый цикл независим
      каждая итерация может выполняться в отдельном потоке
  • Перерисовать нить

    • Другие потоки работают и обновляют структуры данных, но один поток перерисовывает экран.
  • Тема главного события

    • Несколько потоков могут генерировать события
    • Один поток должен обрабатывать события (поскольку важен порядок)
    • Следует попытаться отделить поток событий / перерисовать поток
      Это (помогает) предотвращает зависание пользовательского интерфейса
      Но может вызвать чрезмерное повторное рисование, если не будет сделано тщательно.
  • Рабочая группа

    • Набор потоков ожидает задания в очереди.
    • Поток извлекает один рабочий элемент из очереди (ожидание, если ни один не доступен).
      Поток работает над одним рабочим элементом до завершения
      После завершения поток возвращается в очередь.
2 голосов
/ 12 декабря 2012

Шаблоны параллельного выполнения

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

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

Существует много часто используемых шаблонов, таких как: Map-Reduce, Fork-Join, Pipeline или Parallel Loop ...

документы

«Структурное параллельное программирование с детерминированными паттернами» - это документ, в котором обсуждаются эти паттерны. Вы также можете увидеть «MHPM: мультимасштабная модель гибридного программирования: гибкая методология распараллеливания», в которой описывается реализация этого подхода на языке C ++ под названием XPU.

библиотека

XPU - это библиотека C ++ на основе задач, состоящая из набора повторно используемых шаблонов выполнения. Это позволяет выразить несколько типов параллелизма на нескольких уровнях детализации в рамках одной однородной модели программирования. Он прост в использовании и иллюстрирует интерес к использованию шаблонов для разработки параллельных программ.

Например, оно позволяет выражение:

  1. Шаблон параллелизма задачи:

    Простой или иерархический шаблон выполнения Fork / Join с некоторыми функциями, такими как как автоматическое обнаружение и защита общих данных.

  2. Шаблон параллелизма данных:

    Шаблон параллельного цикла с масштабируемым разделением данных.

  3. Шаблон временного параллелизма:

    Схема выполнения трубопровода.

2 голосов
/ 01 ноября 2008

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

а) иметь кластер, а не многопроцессорную систему, или

b) если у вас много процессоров (скажем,> 60) и высокая степень неоднородной памяти

Для совместно используемой памяти общим решением является использование потоков; их легко понять как концепцию и легко использовать в API (но их трудно отладить).

Для общего доступа вы используете какой-то тип сообщений. В высокопроизводительных вычислениях MPI считается промежуточным программным обеспечением для обмена сообщениями.

Затем вам также необходимо спроектировать архитектуру для параллельных действий. Наиболее распространенный подход (опять же, потому что его легко понять) - это модель фермер-рабочий (a.k.a. master-slave).

1 голос
/ 12 октября 2017

У вас есть основы, которые связывают параллелизм с частями программы. C ++ 17 получает многие из них (например, параллельные версии foreach, sort, find и friends, map_reduce, map, Reduce, prefix_sum ...). См. Расширения C ++ для параллелизма

Тогда у вас есть такие предметы, как продолжения. Подумайте std :: future , но продолжайте. Есть несколько способов их реализации (у boost есть хороший, так как у std нет метода next (...) или then (...), но большим преимуществом является то, что не нужно ждать, чтобы выполнить следующую задачу

auto fut = async([]( ){..some work...} ).then( [](result_of_prev ){...more work} ).then... ;
fut.wait( );

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

Так что с этим параллелизмом на основе задач действительно приятно. С помощью планировщика задач вы просто пропускаете задачи и уходите. У них могут быть методы, такие как семафор, для обратной связи, но это не обязательно. Оба строительные блоки Intel Thread и Microsoft Parallel Pattern Library имеют средства для этого.

После этого у нас есть шаблон fork / join. Это не подразумевает создание N потоков для каждой задачи. Просто у вас есть эти N, идеально независимые вещи, которые нужно сделать (разветвление), и когда они сделаны, где-то есть точка синхронизации (объединение).

auto semaphore = make_semaphore( num_tasks );
add_task( [&semaphore]( ) {...task1...; semaphore.notify( ); } );
add_task( [&semaphore]( ) {...task2...; semaphore.notify( ); } );
...
add_task( [&semaphore]( ) {...taskN...; semaphore.notify( ); } );
semaphore.wait( );

Сверху вы можете начать видеть шаблон, что это потоковый график. Будущее - (A >> B >> C >> D), а Fork Join - (A | B | C | D). При этом вы можете объединить их, чтобы сформировать график. (A1 >> A2 | B1 >> B2 >> B3 | C1 | D1 >> D2 >> (E1 >> E2 | F1)) Где A1 >> A2 означает, что A1 должен предшествовать A2, а A | B означает, что A и B может работать одновременно. Медленные части находятся в конце графиков / подграфов, где все сходится.

Цель состоит в том, чтобы найти независимые части системы, которым не нужно общаться. Параллельные алгоритмы, как отмечалось выше, почти во всех случаях медленнее, чем их последовательные аналоги, пока рабочая нагрузка не станет достаточно высокой или размер не станет достаточно большим (при условии, что связь не слишком болтливая). Например сортировка. На 4-ядерном компьютере вы получите примерно в 2,5 раза большую или меньшую производительность, потому что слияние является болтливым, требует большой синхронизации и не работает со всеми ядрами после первого раунда слияния. На GPU с очень большим N можно использовать менее эффективную сортировку, такую ​​как Bitonic, и в итоге получается очень быстро, потому что у вас много работников, чтобы справляться с работой, и все спокойно делают свое дело.

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

Но при всех типах параллелизма медлительность приходит от общения. Уменьшите его.

...