Параллелизм уровня петли
Параллелизм уровня петли является формой параллелизма в программном программировании , которая связана с извлечением параллельных задач из петлей . Возможность параллелизма уровня петли часто возникает в вычислительных программах, где данные хранятся в случайного доступа структурах данных . В тех случаях, когда последовательная программа будет выполнять структуру данных и работать по индексам по одному, в программе, использующей параллелизм уровня петли, будет использовать несколько потоков или процессов , которые работают на некоторых или всех индексах одновременно. Такой параллелизм обеспечивает ускорение общего времени выполнения программы, обычно в соответствии с законом Амдала .
Описание
[ редактировать ]Для простых циклов, где каждая итерация не зависит от других, параллелизм уровня петли может быть смущающим параллельным , поскольку параллелизирование требует только назначения процесса для обработки каждой итерации. Тем не менее, многие алгоритмы предназначены для последовательного запуска и сбоя, когда параллельные процессы раны из -за зависимости в коде. Последовательные алгоритмы иногда применимы к параллельным контекстам с небольшой модификацией. Обычно, однако, они требуют синхронизации процесса . Синхронизация может быть либо подразумевающейся через передачу сообщений , либо явное посредством синхронизации примитивов, таких как семафоры .
Пример
[ редактировать ]Рассмотрим следующий код, работающий в списке L
длины n
.
for (int i = 0; i < n; ++i) {
S1: L[i] += 10;
}
Каждая итерация цикла берет значение из текущего индекса L
, и увеличивает это на 10. Если оператор S1
принимает T
Время для выполнения, тогда цикл требует времени n * T
Чтобы выполнить последовательно, игнорируя время, принятое конструкциями петли. Теперь рассмотрим систему с p
процессоры, где p > n
Полем Если n
потоки работают параллельно, время для выполнения всех n
шаги уменьшаются до T
.
Менее простые случаи производят несовместимые, т.е. не сериализируемые результаты. Рассмотрим следующий цикл, работающий в том же списке L
.
for (int i = 1; i < n; ++i) {
S1: L[i] = L[i-1] + 10;
}
Каждая итерация устанавливает текущий индекс как значение предыдущего плюс десять. При последовательном запуске каждая итерация гарантируется, что предыдущая итерация уже будет иметь правильное значение. Благодаря нескольким потокам, планирование процессов и другие соображения предотвращают заказ на выполнение гарантировать, что итерация будет выполняться только после того, как его зависимость будет выполнена. Это вполне может произойти раньше, что приводит к неожиданным результатам. Сериализация может быть восстановлена путем добавления синхронизации, чтобы сохранить зависимость от предыдущих итераций.
Зависимости в коде
[ редактировать ]Есть несколько типов зависимостей, которые можно найти в коде. [ 1 ] [ 2 ]
Тип | Обозначение | Описание |
---|---|---|
Истинная (поток) зависимость | S1 ->T S2
|
Истинная зависимость между S1 и S2 означает, что S1 пишет в местоположении, которое позже читается от S2 |
Анти -зависимость | S1 ->A S2
|
Антизазапада между S1 и S2 означает, что S1 читает из места, позже написанного S2. |
Выходная зависимость | S1 ->O S2
|
Выходная зависимость между S1 и S2 означает, что S1 и S2 записываются в то же место. |
Входная зависимость | S1 ->I S2
|
Входная зависимость между S1 и S2 означает, что S1 и S2 читаются из того же места. |
Чтобы сохранить последовательное поведение петли при запуске параллельно, истинная зависимость должна быть сохранена. Антизазазависимость и зависимость выходной сигналы можно решать, предоставив каждому процессу свою собственную копию переменных (известных как приватизация). [ 1 ]
Пример истинной зависимости
[ редактировать ]S1: int a, b;
S2: a = 2;
S3: b = a + 40;
S2 ->T S3
, это означает, что S2 имеет истинную зависимость от S3, потому что S2 пишет с переменной a
, который S3 читает от.
Пример антизависимости
[ редактировать ]S1: int a, b = 40;
S2: a = b - 38;
S3: b = -1;
S2 ->A S3
, это означает, что S2 обладает антизависимостью на S3, потому что S2 считывается из переменной b
Перед S3 пишет это.
Пример зависимости от вывода
[ редактировать ]S1: int a, b = 40;
S2: a = b - 38;
S3: a = 2;
S2 ->O S3
, это означает, что S2 имеет выходную зависимость от S3, потому что оба записывают в переменную a
.
Пример входной зависимости
[ редактировать ]S1: int a, b, c = 2;
S2: a = c - 1;
S3: b = c + 1;
S2 ->I S3
, это означает, что S2 имеет входную зависимость от S3, потому что S2 и S3 считываются из переменной c
.
Зависимость в петлях
[ редактировать ]Зависимость от петли против зависящей от петли
[ редактировать ]Петли могут иметь два типа зависимости:
- Зависимость от петли
- Зависимость от петли
В зависимости от петли, петли обладают зависимостью от взаимодействия, но не имеют зависимости между итерациями. Каждая итерация может рассматриваться как блок и выполняться параллельно без других усилий по синхронизации.
В следующем примере кода, используемого для обмена значениями двух массива длины n, существует независимая от цикла зависимости S1 ->T S3
.
for (int i = 1; i < n; ++i) {
S1: tmp = a[i];
S2: a[i] = b[i];
S3: b[i] = tmp;
}
В зависимости от петли заявления в итерации цикла зависят от утверждений в другой итерации цикла. Зависимость, получающая петлю, использует модифицированную версию обозначения зависимости, которую можно увидеть ранее.
Пример зависимости от петли, где S1[i] ->T S1[i + 1]
, где i
указывает на текущую итерацию и i + 1
Указывает следующую итерацию.
for (int i = 1; i < n; ++i) {
S1: a[i] = a[i-1] + 1;
}
Цикл переноса графика зависимости
[ редактировать ]Графически графический график зависимости зависимости от петли показывает зависимости от итераций между итерациями. Каждая итерация указана как узел на графике, а направленные ребра показывают истинные зависимости и выходные данные между каждой итерацией.
Типы
[ редактировать ]Существует множество методологий для параллельных петлей.
- Распределенная петля
- Долл Параллелизм
- Докросс параллелизм
- Спираль [ 3 ]
- Параллелизм Dopipe
Каждая реализация немного зависит от того, как синхронизируются потоки, если вообще. Кроме того, параллельные задачи должны быть каким -то образом нанесены на карту с процессом. Эти задачи могут быть выделены статически или динамически. Исследования показали, что балансировка нагрузки может быть лучше достигнута с помощью некоторых алгоритмов динамического распределения, чем при выполнении статически. [ 4 ]
Процесс параллелизации последовательной программы может быть разбит на следующие дискретные шаги. [ 1 ] Каждое бетонное петля-параллелизация ниже неявно выполняет их.
Тип | Описание |
---|---|
Разложение | Программа разбита на задачи, самую маленькую эксплуатационную единицу согласия. |
Назначение | Задачи назначены процессам. |
Оркестровка | Доступ к данным, связь и синхронизация процессов. |
Картирование | Процессы связаны с процессорами. |
Распределенная петля
[ редактировать ]Когда петля имеет зависимость от петли, один из способов параллелизировать ее-это распределить петлю в несколько различных циклов. Заявления, которые не зависят друг от друга, разделены, так что эти распределенные петли могут быть выполнены параллельно. Например, рассмотрим следующий код.
for (int i = 1; i < n; ++i) {
S1: a[i] = a[i-1] + b[i];
S2: c[i] += d[i];
}
Цикл имеет петлю, несущую зависимость S1[i] ->T S1[i+1]
Но S2 и S1 не имеют независимой от цикла зависимости, поэтому мы можем переписать код следующим образом.
loop1: for (int i = 1; i < n; ++i) {
S1: a[i] = a[i-1] + b[i];
}
loop2: for (int i = 1; i < n; ++i) {
S2: c[i] += d[i];
}
Обратите внимание, что теперь Loop1 и Loop2 могут быть выполнены параллельно. Вместо отдельной инструкции выполняются параллельно на разных данных, как в параллелизме уровня данных, здесь разные петли выполняют разные задачи на разных данных. Допустим, время исполнения S1 и S2 будет и тогда время выполнения для последовательной формы вышеупомянутого кода составляет , Теперь, потому что мы разделили два утверждения и размещаем их в двух разных петлях, дает нам время исполнения Полем Мы называем этот тип параллелизма либо функции, либо параллелизмом задачи.
Долл Параллелизм
[ редактировать ]Параллелизм Doall существует, когда утверждения в цикле могут быть выполнены независимо (ситуации, когда не существует зависимости от петли). [ 1 ] Например, следующий код не читается из массива a
, и не обновляет массивы b, c
Полем Никакие итерации не имеют зависимости от любой другой итерации.
for (int i = 0; i < n; ++i) {
S1: a[i] = b[i] + c[i];
}
Допустим, время одного исполнения S1 будет тогда время выполнения для последовательной формы вышеупомянутого кода составляет , Теперь, поскольку параллелизм Doall существует, когда все итерации являются независимыми, ускорение может быть достигнуто путем выполнения всех итераций параллельно, что дает нам время исполнения , которое является временем для одной итерации в последовательном исполнении.
В следующем примере, используя упрощенный псевдо -код, показывает, как цикл может быть параллелизован для самостоятельного выполнения каждой итерации.
begin_parallelism();
for (int i = 0; i < n; ++i) {
S1: a[i] = b[i] + c[i];
end_parallelism();
}
block();
Докросс параллелизм
[ редактировать ]Параллелизм Doacross существует там, где итерации петли параллелизируются путем извлечения расчетов, которые можно выполнять независимо и выполнять их одновременно. [ 5 ]
Существует синхронизация, чтобы обеспечить соблюдение зависимости от цикла.
Рассмотрим следующее, синхронная петля с зависимостью S1[i] ->T S1[i+1]
.
for (int i = 1; i < n; ++i) {
a[i] = a[i-1] + b[i] + 1;
}
Каждая итерация цикла выполняет два действия
- Рассчитать
a[i-1] + b[i] + 1
- Назначить значение
a[i]
Расчет значения a[i-1] + b[i] + 1
, а затем выполнение назначения может быть разложено на две строки (операторы S1 и S2):
S1: int tmp = b[i] + 1;
S2: a[i] = a[i-1] + tmp;
Первая строка, int tmp = b[i] + 1;
, не имеет петлевых зависимости. Цикл может быть параллелизован путем расчета значения температуры параллельно, а затем синхронизировать назначение с a[i]
.
post(0);
for (int i = 1; i < n; ++i) {
S1: int tmp = b[i] + 1;
wait(i-1);
S2: a[i] = a[i-1] + tmp;
post(i);
}
Допустим, время исполнения S1 и S2 будет и тогда время выполнения для последовательной формы вышеупомянутого кода составляет , Теперь, поскольку параллелизм Doacross существует, ускорение может быть достигнуто путем выполнения итераций в трубопроводной манере, что дает нам время исполнения .
Параллелизм Dopipe
[ редактировать ]Параллелизм Dopipe реализует трубопроводной параллелизм для зависимости от петли, где итерация петли распределяется по нескольким синхронизированным петлям. [ 1 ] Цель Dopipe - действовать как сборочная линия, где один этап запускается, как только будет достаточное количество данных на предыдущем этапе. [ 6 ]
Рассмотрим следующее, синхронный код с зависимостью S1[i] ->T S1[i+1]
.
for (int i = 1; i < n; ++i) {
S1: a[i] = a[i-1] + b[i];
S2: c[i] += a[i];
}
S1 должен быть выполнен последовательно, но S2 не имеет контурированной зависимости. S2 может быть выполнен параллельно с использованием параллелизма Doall после выполнения всех расчетов, необходимых S1, последовательно. Однако ускорение ограничено, если это сделано. Лучший подход заключается в параллелизировании так, чтобы S2, соответствующий каждым S1, выполняется, когда указанный S1 закончен.
Реализация трубопровода приводит к следующему набору циклов, где второй цикл может выполняться для индекса, как только первый цикл завершит соответствующий индекс.
for (int i = 1; i < n; ++i) {
S1: a[i] = a[i-1] + b[i];
post(i);
}
for (int i = 1; i < n; i++) {
wait(i);
S2: c[i] += a[i];
}
Допустим, время исполнения S1 и S2 будет и тогда время выполнения для последовательной формы вышеупомянутого кода составляет , Теперь, поскольку существует параллелизм допипей, ускорение может быть достигнуто путем выполнения итераций в трубопроводной манере, что дает нам время исполнения , где P - количество процессора параллельно.
Смотрите также
[ редактировать ]- Параллелизм данных
- Докросс параллелизм
- Задача параллелизм
- Параллелизм с использованием различных типов моделей памяти, таких как общие и распределенные и передача сообщения
Ссылки
[ редактировать ]- ^ Jump up to: а беременный в дюймовый и Солихин, Ян (2016). Фуралиты параллельной архитектуры Boca Raton, FL: CRC Press. ISBN 978-1-4822-1118-4 .
- ^ Гофф, Джина (1991). «Практическое тестирование зависимости». Труды конференции ACM Sigplan 1991 по дизайну и реализации языка программирования - PLDI '91 . С. 15–29. doi : 10.1145/113445.113448 . ISBN 0897914287 Полем S2CID 2357293 .
- ^ Мерфи, Найл. «Обнаружение и эксплуатацию параллелизма в петлях Doacross» (PDF) . Кембриджский университет . Получено 10 сентября 2016 года .
- ^ Кави, Кришна. «Параллелизация Doall и Doacross Loops-A» .
{{cite journal}}
: CITE Journal требует|journal=
( помощь ) - ^ Unnikrishnan, Priya (2012), «Практический подход к параллелизации Doacross», Параллельная обработка Euro-Par 2012 , лекционные заметки в компьютерных науках, вып. 7484, с. 219–231, doi : 10.1007/978-3-642-32820-6_23 , ISBN 978-3-642-32819-0 , S2CID 18571258
- ^ «DOPIPE: эффективный подход к параллелизации моделирования» (PDF) . Intel . Получено 13 сентября 2016 года .