This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
1. Неупоредоченный список значений равномерно распределён по памяти процессоров распределённой ВС.
2. По окончании сортировки:
▪ Списки, хранящиеся в памяти процессоров, отсортированы.
▪ Значение последнего элемента в списке процессора Pi меньше или равно значению первого элемента процессора Pi+1, для 0 ≤ i ≤ p – 2.
▪ Отсортированные значения не обязательно должны быть равномерно распределены по процессорам.
≤ ≤ ≤
P0 P1 P2 P3
4
1. Неотсортированные значения равномерно распределены по процессам.
2. Выбирается опорное значение с одного из процессов и рассылается всем процессам.
3. Каждый процесс разделяется неотсортированные значения на два списка: те, которые меньше или равны опорному значению, и те, которые больше опорного значения.
4. Все процессы разделяются на две части: первая половина и вторая половина. Каждому процессору из первой половины соответствует процессор из второй половины.
5. Каждый процесс i из первой половины передаёт свои значения, которые больше опорного, своей паре – процессору j из второй половины. В ответ процессор получает от j значения, которые меньше или равны опорному.
Таким образом, после выполнения этого шага максимальное значение, которое содержится у процессора i, меньше, чем минимальное значение в массиве процессора j.
6. Алгоритмы выполняется рекурсивно для каждой половины процессоров: в каждой половине процессоров выбирается опорный элемент, он рассылается всем процессам этой половины.
7. Когда деление на группы больше невозможно, каждый процесс сортирует свои элементы.
Алгоритм параллельной быстрой сортировки
5
P0 P1 P2 P3 P4 P5 P6 P7
a
1. Неотсортированные значения равномерно распределены по процессам.
a
2. Первый процесс выбирает опорный элемент и рассылает его остальным.
3. Каждый процесс разделяет свой массив на две части по опорному элементу.
a
4, 5. Процессы разделяются на две половины, и каждый процесс из первой половины передаёт значение, которые больше опорного элемента, своей паре-процессу из второй половины.
>≤ ≤ > ≤ > ≤ > > ≤ > ≤ > ≤ >≤
a >≤ ≤ > ≤ > ≤ > > ≤ > ≤ > ≤ >≤
Алгоритм параллельной быстрой сортировки
6
P0 P1 P2 P3 P4 P5 P6 P7
a
1. Неотсортированные значения равномерно распределены по процессам.
a
2. Первый процесс выбирает опорный элемент и рассылает его остальным.
a >≤ ≤ > ≤ > ≤ > > ≤ > ≤ > ≤ >≤
a
4, 5. Процессы разделяются на две половины, и каждый процесс из первой половины передаёт значение, которые больше опорного элемента, своей паре-процессу из второй половины.
>≤ ≤ > ≤ > ≤ > > ≤ > ≤ > ≤ >≤
3. Каждый процесс разделяет свой массив на две части по опорному элементу.
Алгоритм параллельной быстрой сортировки
7
P0 P1 P2 P3 P4 P5 P6 P7
a
1. Неотсортированные значения равномерно распределены по процессам.
a
2. Первый процесс выбирает опорный элемент и рассылает его остальным.
a >≤ ≤ > ≤ > ≤ > > ≤ > ≤ > ≤ >≤
a
4, 5. Процессы разделяются на две половины, и каждый процесс из первой половины передаёт значение, которые больше опорного элемента, своей паре-процессу из второй половины.
>≤ ≤ > ≤ > ≤ > > ≤ > ≤ > ≤ >≤
3. Каждый процесс разделяет свой массив на две части по опорному элементу.
Алгоритм параллельной быстрой сортировки
7. Каждый процесс сортирует свои элементы.
8
Алгоритм гипербыстрой сортировки
1. Неотсортированные значения равномерно распределены по процессам.
2. Каждый процесс сортирует свою часть массива.
3. Одиз из процессов в качестве опорного элемента выбирает медиану из своих отсортированных значений и отправляет его остальным процессам.
4. Каждый процесс разделяется неотсортированные значения на два списка: те, которые меньше или равны опорному значению, и те, которые больше опорного значения.
5. Процессы раздялеются на две половины, и каждый процесс i из первой половины передаёт свои значения, которые больше опорного, своей паре – процессору j из второй половины. В ответ процессор получает от j значения, которые меньше или равны опорному.
6. Каждый процесс объединяет подмассив, который у него был, и значения, полученные от другого процесса, и затем сортирует получившийся массив.
7. Алгоритмы выполняется рекурсивно для каждой половины процессоров: в каждой половине процессоров выбирается опорный элемент, он рассылается всем процессам этой половины.
1. Обнаружить самое длинное незаконченное слово в паззле и найти слово в словаре, которое подходит по размеру. Если в словаре несколько слов, выбираем случайное.
2. На каждом следующем шаге находим самое длинное незаконченное слово с как минимум одним символом и отыскиваем в словаре слово нужной длины, которое подходит к данным известным буквам.
Различные варианты назначения слова на каждом шаге формируют дерево пространства состояний. Корень дерева – пустой паззл. Потомки корня – семибуквенные слова.
Каждые последующие узлы дерева – возможные назначения слов из словаря на незаконченные слова в паззле.
Суть поиска с возвратом заключается в следующем:
Если в какой-то момент наш очередной выбор приводит к ситуации, когда задачу решить невозможно, мы возвращаемся к предыдущему уровню дерева и рассматриваем альтернативное решение.
1 2 3 4 5 6
7
9
12
16
18
13
11
10
14 15
17
19
12
Поиск с возвратом
1 2 3 4 5 6
7
9
12
16
18
13
11
10
14 15
17
19
1 2 3 4 5 6
7
9
12
16
18
13
11
10
14 15
17
19
TROLLEY
1 2 3 4 5 6
7
9
12
16
18
13
11
10
14 15
17
19
TROLLEY
1 2 3 4 5 6
7
9
12
16
18
13
11
10
14 15
17
19
TROLLEY
C L S E T S
C R Q U E T
1 2 3 4 5 6
7
9
12
16
18
13
11
10
14 15
17
19
TROLLEY
C R Q U E T
TR
MPED
13
Простейшая параллельная версия алгоритма
Поддерево поиска для процесса 0
Поддерево поиска для процесса 1
Поддерево поиска для процесса 2
Поддерево поиска для процесса 3
▪ Если p = bk (b – число потомков каждого узла), то каждый процесс сначала последовательного доходит до уровня k, а затем начинает идти по одному из k поддеревьев на этом уровне.
▪ Такой алгоритм подходит только для числа процессоров p, равного степени b.
▪ Кроме того, дерево, как правило несбалансировано и поддеревья различаются по сложности.
14
Поиск с возвратом – параллельный алгоритм
▪ Если p = bk (b – число потомков каждого узла), то каждый процесс доходит до уровня k, и затем число поддеревьев равномерно распределяется между процессами по принципу round-robin.
kk
P0
P0P0
P1
P2
P3
P1
P2
P3
P1
P2
P3
P0
P1
P2
P3
P0P1
P3P2P2
P2P1 P1P3P0 P0
15
▪ Если p = bk (b – число потомков каждого узла), то каждый процесс доходит до уровня k, и затем число поддеревьев равномерно распределяется между процессами.
kk
Уско
рени
е
2
4
6
2 4 6 8 10Глубина
Для каждого количества процессоров p и каждого значения b можно определить оптимальную глубину, до которой должны доходить процессы перед распределением поддеревьев.
Поиск с возвратом – параллельный алгоритм
16
Поиск с возвратом – параллельный алгоритм
cutoff_depth; // Глубина, на которой поддеревья распределяются между процессамиcutoff_count; // Число узлов на глубине cutoff_depthdepth; // Глубина, до которой необходимо выполнять поискmoves; // Записи позиций в дереве поискаrank; // Ранг процессаp; // Число процессов
ParallelBacktrack(board, level) { if level = depth { if board – есть решение задачи { PrintSolution(moves) } } else { if level = cutoff_depth { cutoff_count ⟵ cutoff_count + 1 if cutoff_count mod p ≠ rank { // Если это не мой узел return // то закончить выполнение } }
possible_moves ⟵ CountMoves(board) // Количество возможных решений for i ⟵ 1 to possible_moves { MakeMove(board, i) // Сделать ход moves[level] ⟵ i // Записать ход ParallelBacktrack(board, level + 1) UnmakeMove(board, i) } }}
17
Поиск с возвратом – параллельный алгоритм
cutoff_depth; // Глубина, на которой поддеревья распределяются между процессамиcutoff_count; // Число узлов на глубине cutoff_depthdepth; // Глубина, до которой необходимо выполнять поискmoves; // Записи позиций в дереве поискаrank; // Ранг процессаp; // Число процессов
ParallelBacktrack(board, level) { if level = depth { if board – есть решение задачи { PrintSolution(moves) } } else { if level = cutoff_depth { cutoff_count ⟵ cutoff_count + 1 if cutoff_count mod p ≠ rank { // Если это не мой узел return // то закончить выполнение } }
possible_moves ⟵ CountMoves(board) // Количество возможных решений for i ⟵ 1 to possible_moves { MakeMove(board, i) // Сделать ход moves[level] ⟵ i // Записать ход ParallelBacktrack(board, level + 1) UnmakeMove(board, i) } }}
Алгоритм не позволяет обнаружить завершение работы одного из процессов
18
Неправильный способ обнаружения завершения
1. Процесс А нашёл решение и отправляет сообщения всем процессам, после чего вызывает функцию MPI_Finalize.
2. Процесс В находит другое решение и отправляет сообщения остальным процессам до получения сообщения от процесса А.
3. Если процесс В попытается отправить сообщение процессу А после того, как процесс А вызвал MPI_Finalize, случится ошибка времени выполнения.
⇒ Поэтому метод обнаружения завершения, основанный на отправки сообщений всем процессам, некорректен и может привести к аварийному завершению программы.
19
Алгоритм Дейкстры обнаружения распределённого завершения
1. Процессы организованы в логическое кольцо.2. Процесс i узнаёт состояние системы путём отправки сообщения следующему
процессу.3. Когда сообщение возвращается процессу i, то он может определить, безопасно ли
завершать работу.▪ Каждый процесс имеет цвет и число сообщений. Когда процесс начинает
выполнение, он белого цвета и его счетчик сообщений равен нулю. ▪ Процесс становится чёрным, когда он отправляет или получает сообщение. Когда
процесс отправляет сообщение, он увеличивает его счетчик сообщений, и когда он принимает сообщение, он уменьшает счетчик.
▪ Как только все процессы становятся белыми и сумма сообщений равна нулю, значит нет текущих сообщений в системе и можно завершать процессы.
▪ Когда процесс получает сообщение, он добавляет значение счетчика в нём к счетчику в сообщении.
▪ Если процесс чёрный, он изменяет цвет сообщения на чёрный. Если процесс белый, он не изменяет цвет сообщения.
▪ Процесс изменяет свой цвет на белый и отправляет обновлённое сообщение следующему процессу.
▪ Если сообщение белое, процесс белый и сумма счетчика сообщения и счетчика процесса равны нулю, в этом случае процессы безопасно завершать.
20
Алгоритм Дейкстры обнаружения распределённого завершения
-1
-1
-17
-2
-2
1 4
32
0
-2
-1
-2
-2
77
5
7
-2 75
2-2-2
0
2-2 Можно
завершать!
0
1
21
Обнаружение завершение в алгоритме поиска с возвратом
1. Все процессы начинают выполнять поиск со счетчиками сообщений, установленными в 0.
2. Когда процесс находит решение, он отправляет сообщение “решение найдено” процессу 0 и устанавливает свой счетчик на 1.
3. Когда процесс 0 получает сообщение “решение найдено”, он уменьшает свой счетчик сообщений.
4. После этого процесс 0 инициирует алгоритм обнаружения завершения.5. Когда процесс получает сообщение “решение найдено”, он прекращает выполнение
поиска.6. Когда процесс 0 получает сообщение и определяет, что в системе больше никакой
процесс не отправляет сообщение, он посылает сообщение “завершение” остальным процессам и выполняет MPI_Finalize.
Параллельный ввод-вывод в MPI
23
Непараллельный ввод-вывод
P0 P1 P2 P3
▪ Не параллельное выполнение.▪ Производительность хуже, чем в случае последовательного ввода-вывода.▪ Можно использовать тот же код, что и в последовательных программах
24
Независимый параллельный ввод-вывод
P0 P1 P2 P3
▪ За: параллелизм.▪ Против: необходимо управлять многими файлами малого размера.▪ Тот же код, что и в последовательных программах.
25
Совместный параллельный ввод-вывод
P0 P1 P2 P3
▪ Паралелизм.▪ Можно реализовать только при помощи MPI.
26
Совместный параллельный ввод-вывод – пример
#include <stdio.h>#include <mpi.h>
int main(int argc, char **argv) { MPI_File file; int buf[BUFSIZE], rank;
// Коллективная операция открытия файла MPI_File_open(MPI_COMM_WORLD, "file", MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &file);
if (rank == 0) // Ввод-вывод в файл – независимая (дифференцированная) операция MPI_File_write(file, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE);
// Коллективная операция закрытия файла MPI_File_close(&file); MPI_Finalize();
return 0;}
27
Запись в файл
▪ Для записи используются функции MPI_File_write или MPI_File_write_at▪ При открытии используются флаги MPI_MODE_WRONLY или MPI_MODE_RDWR▪ Если файл до этого не существовал, необходимо добавить флаг
MPI_MODE_CREATE
MPI_File_seekMPI_File_readMPI_File_write
– как при обычном вводе-выводе в Linux
MPI_File_read_atMPI_File_write_at
– комбинация перехода в определённую позицию и ввода-вывода
28
Совместный параллельный ввод-вывод со смещениями
#include <stdio.h>#include <mpi.h>
int main() { MPI_Status status; MPI_File file; MPI_Offset offset;
▪ Каждый процесс описывает свою часть файла, за которую он ответственен (с помощью смещения).
▪ Только собственная часть файла видна каждому процессу. Все операции производятся с этой частью файла.
▪ Такой ввод-вывод повсеместно используется в параллельных программах (например, используется для хранения распределённых массивов).
Файл в этом случае задаётся функцией MPI_File_set_view тремя параметрами:1. displacement – число байт, пропущенных с начала файла (например, заголовок)
2. etype – базовый тип данных
3. filetype – какая область файла видима для процесса