86
Лекция 2. Коллективные операции в стандарте MPI Пазников Алексей Александрович Параллельные вычислительные технологии СибГУТИ (Новосибирск) Осенний семестр 2015 www: cpct.sibsutis.ru/~apaznikov/teaching q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall

Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Embed Size (px)

Citation preview

Page 1: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Лекция 2. Коллективные операции в стандарте MPI

Пазников Алексей Александрович

Параллельные вычислительные технологии СибГУТИ (Новосибирск) Осенний семестр 2015

www: cpct.sibsutis.ru/~apaznikov/teaching q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall

Page 2: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

2

Коллективные операции

▪ Коллективные коммуникации – это коммуникационные операции, в которых одновременно участвуют все процессы одного коммуникатора.

▪ Коллективные операции – это высокоуровневые операции, по сравнению с дифференцированными обменами (поскольку, как правило, реализованы на основе дифференцированных обменов).

Коммуникатор

P0P1

P2

P3

P4

P5

P6

Page 3: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

3

Коллективные операции

▪ Коллективные коммуникации – это коммуникационные операции, в которых одновременно участвуют все процессы одного коммуникатора.

▪ Коллективные операции – это высокоуровневые операции, по сравнению с дифференцированными обменами (поскольку, как правило, реализованы на основе дифференцированных обменов).

Коммуникатор

▪ Коллективные операции бывают блокирующими и неблокирующими.

▪ В коллективных операциях отсутствуют тэги.

▪ Буфер получения должен иметь в точности необходимый размер.

P0P1

P2

P3

P4

P5

P6

Page 4: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

4

Барьерная синхронизация

int MPI_Barrier(MPI_Comm comm);

Barrier Barrier

Barrier Barrier

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

P0 P0

P0 P0

P1 P1

P2

P2 P2

P1P1

P2

P3 P3

P3P3

Page 5: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

5

Вычисление числа пи методов численного интегрирования

▪ Разделить интервал на субинтервалы

▪ Распределить интервалы по процессам

▪ Каждый процесс рассчитывает частичную сумму

▪ Сложить все частичные суммы для вычисления числа пи

1

1

n сегментов

1. Каждый из сегментов имеет ширину w = 1 / n.

2. Расстояние для i-го сегмента от 0: di = i * w.

3. Высота i-го сегмента: sqrt(1 – di2).

w

sqrt(

1 –

d i2 )

di = i * w

Пример: вычисление числа пи

Page 6: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

6

Параллельная редукция (reduce, reduction)

int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);

P1P0

5 31

P2

-6

P4

12

До MPI_Reduce

P0 P1 P2 P4

31

После MPI_Reduce

42

5 -6 12

sendbuf

recvbuf

Page 7: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

7

int MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

P1P0

5 31

P2

-6

P4

12

До MPI_Allreduce

P0 P1 P2 P4

31

После MPI_Allreduce

42

5 -6 12

42 42 42

sendbuf

recvbuf

Параллельная редукция (reduce, reduction)

Page 8: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

8

int MPI_Scan(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

P1P0

1 2

P2

3

P4

4

До MPI_Scan

P0 P1 P2 P4

2

После MPI_Scan

3

1 3 4

1 6 10

sendbuf

recvbuf

Префиксная сумма (scan, prefix sum)

Page 9: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

9

#include <mpi.h>#include <math.h>

int main(int argc, char *argv[]) { // ... int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size);

int n = atoi(argv[1]); double w = 1.0 / (double) n; double mypi = 0.0; for (i = rank + 1; i <= n; i += numprocs) mypi += w * sqrt(1 – (((double) i / n) * ((double) i / n));

// Собрать в 0 процессе итоговую сумму MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

pi *= 4; if (rank == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - M_PI)); // ...}

Пример: вычисление числа пи

Page 10: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

10

#include <mpi.h>#include <math.h>

int main(int argc, char *argv[]) { // ... int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size);

int n = atoi(argv[1]); double w = 1.0 / (double) n; double mypi = 0.0; for (i = rank + 1; i <= n; i += numprocs) mypi += w * sqrt(1 – (((double) i / n) * ((double) i / n));

// Собрать в 0 процессе итоговую сумму MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

pi *= 4; if (rank == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - M_PI)); // ...}

pi is approximately 3.1415906524138242, Error is 0.0000020011759689

Пример: вычисление числа пи

Page 11: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

11

Широковещательная рассылка (broadcast)

int MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm);

P0 P1

B A C H

P2 P4

P0

B A C H

P1

B A C H

P2

B A C H

P4

B A C H

root

▪ Корень (root) должен быть одинаковым во всех вызовах MPI_Bcast.

До MPI_Bcast

После MPI_Bcast

buf

Page 12: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

12

// ...

// Сгенерировать в 0 процессе случайное число n if (rank == 0) { srand(time(NULL)); n = (rand() / (float) RAND_MAX) * MAX_N; printf("process 0 generated n as %d\n", n); }

// Отправить число n всем процессам MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

double w = 1.0 / (double) n; double mypi = 0.0; for (i = rank + 1; i <= n; i += numprocs) mypi += w * sqrt(1 – (((double) i / n) * ((double) i / n));

// Собрать в 0 процессе итоговую сумму MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

pi *= 4; if (rank == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - M_PI)); // ...

Пример: вычисление числа пи

Page 13: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление числа пи

13

// ...

// Сгенерировать в 0 процессе случайное число n if (rank == 0) { srand(time(NULL)); n = (rand() / (float) RAND_MAX) * MAX_N; printf("process 0 generated n as %d\n", n); }

// Отправить число n всем процессам MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

double w = 1.0 / (double) n; double mypi = 0.0; for (i = rank + 1; i <= n; i += numprocs) mypi += w * sqrt(1 – (((double) i / n) * ((double) i / n));

// Собрать в 0 процессе итоговую сумму MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

pi *= 4; if (rank == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - M_PI)); // ...process 0 generated n as 966321536pi is approximately 3.1415926515201327, Error is 0.0000000020696604

Page 14: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

14

P1P0 P2 P4

До вычисления среднего арифметического

После вычисления среднего арифметического

array

Корневой процесс генерирует случайный массив вещественных чисел. Необходимо расчитать среднее арифметическое этих чисел и сохранить результат в корневом процессе.

Пример: вычисление среднего арифметического чисел

P1P0 P2 P4

average

array

Page 15: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

P1

15

Распределение данных (scatter)

int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);

P0 P1

B A C H

P2 P4

P0

B A

P2

C

P4

H

root

B A C H

До MPI_Scatter

После MPI_Scatter

sendbuf

recvbuf

Page 16: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

P1

16

Сбор данных (gather)

int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);

P0

B A

P2

C

P4

H

До MPI_Gather

P0 P1 P2 P4

B A C H

После MPI_Gather

rootB A C H

recvbuf

sendbuf

Page 17: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

P1

17

int MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm);

P0

B A

P2

C

P4

H

До MPI_Gather

P0

B A C H

P1 P2

B A C H

P4

B A C HB A C H

После MPI_Gather

rootB A C H

sendbuf

recvbuf

Сбор данных (gather)

Page 18: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

18

P0 P1 P2 P4

1. Генерация случайного массива чисел на корневом (нулевом) процессе.

Page 19: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

19

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.

P0 P1 P2 P4

Page 20: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

20

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.

P0 P1 P2 P4

Page 21: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

21

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Сбор (gather) всех промежуточных средних значений на корневом процессе.

P0 P1 P2 P4

Page 22: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

22

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Сбор (gather) всех промежуточных средних значений на корневом процессе. 5. Вычисление корневым процессом финального среднего значения.

P0 P1 P2 P4

Page 23: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

23

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Сбор (gather) всех промежуточных средних значений на корневом процессе. 5. Вычисление корневым процессом финального среднего значения.

P0 P1 P2 P4

2. Scatter

4. Gather

1. Генерация

3. Вычисление

5. Вычисление

Page 24: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

24

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать среднее значения чисел подмножества на каждом процессеfloat sub_avg = compute_avg(sub_rand_nums, elements_per_proc);

// Собрать все промежуточные значения на корневом процессеfloat *sub_avgs = NULL;if (rank == 0) { sub_avgs = malloc(sizeof(float) * world_size);}MPI_Gather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать общее среднее значение всех чиселif (world_rank == 0) { float avg = compute_avg(sub_avgs, commsize);}

Page 25: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

25

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать среднее значения чисел подмножества на каждом процессеfloat sub_avg = compute_avg(sub_rand_nums, elements_per_proc);

// Собрать все промежуточные значения на корневом процессеfloat *sub_avgs = NULL;if (rank == 0) { sub_avgs = malloc(sizeof(float) * commsize);}MPI_Gather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать общее среднее значение всех чиселif (world_rank == 0) { float avg = compute_avg(sub_avgs, commsize);}

Генерируем исходный “глобальный” массив

Выделяем память под массив промежуточных результатов

Выделяем память под “локальный” массив

Page 26: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

26

// Сгенерировать массив случайных чисел в диапазоне от 0 до 1float *create_rand_nums(int num_elements) { float *rand_nums = (float *)malloc(sizeof(float) * num_elements); assert(rand_nums != NULL); int i;

for (i = 0; i < num_elements; i++) { rand_nums[i] = (rand() / (float)RAND_MAX); } return rand_nums;}

// Расчитать среднее значение для массива чиселfloat compute_avg(float *array, int num_elements) { float sum = 0.f; int i;

for (i = 0; i < num_elements; i++) { sum += array[i]; } return sum / num_elements;}

Page 27: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

27

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать среднее значения чисел подмножества на каждом процессеfloat sub_avg = compute_avg(sub_rand_nums, elements_per_proc);

// Собрать все промежуточные значения на корневом процессеfloat *sub_avgs = NULL;if (rank == 0) { sub_avgs = malloc(sizeof(float) * commsize);}MPI_Gather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать общее среднее значение всех чиселif (world_rank == 0) { float avg = compute_avg(sub_avgs, commsize);}Average of all elements is 0.499741

Page 28: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

28

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать среднее значения чисел подмножества на каждом процессеfloat sub_avg = compute_avg(sub_rand_nums, elements_per_proc);

float *sub_avgs = NULL;

// Выделить память под массив промежуточных результатов на всех процессахsub_avgs = malloc(sizeof(float) * commsize);

// Собрать все промежуточные значения на всех процессахMPI_Allgather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать общее среднее значение всех чисел на всех процессахfloat avg = compute_avg(sub_avgs, commsize);

Page 29: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: вычисление среднего арифметического чисел

29

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать среднее значения чисел подмножества на каждом процессеfloat sub_avg = compute_avg(sub_rand_nums, elements_per_proc);

float *sub_avgs = NULL;

// Выделить память под массив промежуточных результатов на всех процессахsub_avgs = malloc(sizeof(float) * commsize);

// Собрать все промежуточные значения на всех процессахMPI_Allgather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Расчитать общее среднее значение всех чисел на всех процессахfloat avg = compute_avg(sub_avgs, commsize);

Avg of all elements from proc 0 is 0.500601Avg of all elements from proc 4 is 0.500601Avg of all elements from proc 1 is 0.500601Avg of all elements from proc 5 is 0.500601Avg of all elements from proc 2 is 0.500601Avg of all elements from proc 6 is 0.500601Avg of all elements from proc 3 is 0.500601Avg of all elements from proc 7 is 0.500601

Page 30: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

30

Параллельная редукция (reduce, reduction)

int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);

P1P0

5 31

P2

-6

P4

12

До MPI_Reduce

P0 P1 P2 P4

31

После MPI_Reduce

42

5 -6 12

sendbuf

recvbuf

Page 31: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

31

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Редукция (reduce) всех промежуточных средних значений на корневом процессе. 5. Вычисление финального значения среднего арифметического на корневом процессе.

P0 P1 P2 P4

Вычисление среднего арифметического чисел с помощью MPI_Reduce

Page 32: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

32

1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное

количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Редукция (reduce) всех промежуточных средних значений на корневом процессе. 5. Вычисление финального значения среднего арифметического на корневом процессе.

P0 P1 P2 P4

2. Scatter

4. Reduce

1. Генерация

3. Вычисление

Вычисление среднего арифметического чисел с помощью MPI_Reduce

5. Вычисление

Page 33: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

33

Вычисление среднего арифметического чисел с помощью MPI_Reduce

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Процессы расчитывают локальную суммуfloat local_sum = 0;for (i = 0; i < num_elements_per_proc; i++) local_sum += sub_rand_nums[i];

// Распечатать промежуточные результаты для каждого процессаprintf("Local sum for process %d - %f, avg = %f\n", world_rank, local_sum, local_sum / num_elements_per_proc);

// Редукция всех значений локальной суммы в глобальную суммуfloat global_sum;MPI_Reduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);

// Напечатать результатfloat global_avg = global_sum / (world_size * num_elements_per_proc);if (world_rank == 0) printf("Total sum = %f, avg = %f\n", global_sum, global_avg);

Page 34: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

34

Вычисление среднего арифметического чисел с помощью MPI_Reduce

if (rank == 0) rand_nums = create_rand_nums(elements_per_proc * commsize);

// Создать буфер, хранящий подмножество случайных чиселfloat *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);

// Распределить случайные числа между процессамиMPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums, elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);

// Процессы расчитывают локальную суммуfloat local_sum = 0;for (i = 0; i < num_elements_per_proc; i++) local_sum += sub_rand_nums[i];

// Распечатать промежуточные результаты для каждого процессаprintf("Local sum for process %d - %f, avg = %f\n", world_rank, local_sum, local_sum / num_elements_per_proc);

// Редукция всех значений локальной суммы в глобальную суммуfloat global_sum;MPI_Reduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);

// Напечатать результатfloat global_avg = global_sum / (world_size * num_elements_per_proc);if (world_rank == 0) printf("Total sum = %f, avg = %f\n", global_sum, global_avg);

Local sum for process 3 - 499834.812500, avg = 0.499835Local sum for process 1 - 500138.093750, avg = 0.500138Local sum for process 5 - 500449.437500, avg = 0.500449Local sum for process 4 - 500161.187500, avg = 0.500161Local sum for process 7 - 499837.125000, avg = 0.499837Local sum for process 6 - 498977.281250, avg = 0.498977Local sum for process 2 - 500126.375000, avg = 0.500126Local sum for process 0 - 500003.750000, avg = 0.500004Total sum = 3999528.000000, avg = 0.499941

Page 35: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

35

P1P0 P2 P4

До вычисления стандартного отклонения

После вычисления стандартного отклонения

array

Корневой процесс генерирует случайный массив вещественных чисел. Необходимо расчитать среднее арифметическое этих чисел и сохранить результат в корневом процессе.

Пример: вычисление стандартного отклонения

P1P0 P2 P4

standard deviation

array

Page 36: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

36

int MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

P1P0

5 31

P2

-6

P4

12

До MPI_Allreduce

P0 P1 P2 P4

31

После MPI_Allreduce

42

5 -6 12

42 42 42

sendbuf

recvbuf

Параллельная редукция (reduce, reduction)

Page 37: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

37

P0 P1 P2 P4

Пример: вычисление стандартного отклоненияЭлементы массива распределены между процессами.

Page 38: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

38

1. Каждый процесс рассчитывает среднее своего подмножества чисел.

P0 P1 P2 P4

Пример: вычисление стандартного отклонения

Page 39: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

39

1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах.

P0 P1 P2 P4

Пример: вычисление стандартного отклонения

Page 40: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

40

1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.

P0 P1 P2 P4

Пример: вычисление стандартного отклонения

Page 41: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

41

1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах.

P0 P1 P2 P4

Пример: вычисление стандартного отклонения

Page 42: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

42

1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах.5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе.

P0 P1 P2 P4

Пример: вычисление стандартного отклонения

Page 43: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

43

P0 P1 P2 P4

Пример: вычисление стандартного отклонения1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах.5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе.6. Вычисление значения стандартного отклонения.

Page 44: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

44

1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе6. Вычисление значения стандартного отклонения.

P0 P1 P2 P4

Пример: вычисление стандартного отклонения

1. Вычисление локальной суммы

2. Allreduce

5. Reduce

6. Вычисление стандартного отклонения

3. Вычисление среднего арифметического

4. Вычисление промежуточной суммы

Page 45: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

45

// ...

// Процессы расчитывают локальную суммуfloat local_sum = 0;for (i = 0; i < num_elements_per_proc; i++) local_sum += sub_rand_nums[i];

// Редукция всех локальных сумм для получения глобальной суммы на всех процессахfloat global_sum;MPI_Allreduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, MPI_COMM_WORLD);float mean = global_sum / (num_elements_per_proc * world_size);

// Вычисление локальной суммы значений xi - xaverage на всех процессахfloat local_sq_diff = 0;for (i = 0; i < num_elements_per_proc; i++) local_sq_diff += (rand_nums[i] - mean) * (rand_nums[i] - mean);

// Редукция для получения глобальной суммы значений xi - xaverage на корневом процессеfloat global_sq_diff;MPI_Reduce(&local_sq_diff, &global_sq_diff, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);

// Вычисление финального значения стандартного отклонения на нулевом процессеif (world_rank == 0) { float stddev = sqrt(global_sq_diff / (num_elements_per_proc * world_size)); printf("Mean - %f, Standard deviation = %f\n", mean, stddev);}

Пример: вычисление стандартного отклонения

Page 46: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

46

// ...

// Процессы расчитывают локальную суммуfloat local_sum = 0;for (i = 0; i < num_elements_per_proc; i++) local_sum += sub_rand_nums[i];

// Редукция всех локальных сумм для получения глобальной суммы на всех процессахfloat global_sum;MPI_Allreduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, MPI_COMM_WORLD);float mean = global_sum / (num_elements_per_proc * world_size);

// Вычисление локальной суммы значений xi - xaverage на всех процессахfloat local_sq_diff = 0;for (i = 0; i < num_elements_per_proc; i++) local_sq_diff += (rand_nums[i] - mean) * (rand_nums[i] - mean);

// Редукция для получения глобальной суммы значений xi - xaverage на корневом процессеfloat global_sq_diff;MPI_Reduce(&local_sq_diff, &global_sq_diff, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);

// Вычисление финального значения стандартного отклонения на нулевом процессеif (world_rank == 0) { float stddev = sqrt(global_sq_diff / (num_elements_per_proc * world_size)); printf("Mean - %f, Standard deviation = %f\n", mean, stddev);}

Пример: вычисление стандартного отклонения

Mean - 0.499427, Standard deviation = 0.288920

Page 47: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

47

P1P0

0.345

P2 P3

До ранжирования

После ранжирования

number

rank

0.621 0.907 0.034

P1P0

0.345

P2 P3

number

rank1

0.621

2

0.907

3

0.034

0

В памяти каждого процесса имеется некоторое число number. Необходимо проранжировать процессы в соответствии с этим числом: назначить каждому процессу его порядковый номер rank по возрастания числа number.

Page 48: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

48

1. Сгенерировать случайное число на каждом процессе.

P0 P2 P3P1

Page 49: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

49

1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.

P0 P2 P3P1

Page 50: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

50

1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.3. Добавить каждому полученного числу ранг соответствующего процесса

P0 P2 P3

0 1 2 3

P1

Page 51: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

51

1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.3. Добавить каждому полученного числу ранг соответствующего процесса4. Отсортировать ранги процессов в соответствии с числами.

P0 P2 P3

0 1 2 3

2 1 3 0

P1

Page 52: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

52

1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.3. Добавить каждому полученного числу ранг соответствующего процесса4. Отсортировать ранги процессов в соответствии с числами. 5. Отправить (scatter) процессам их порядковые номера.

P0 P2 P3

0 1 2 3

2023

2 1 3 0

3 1 0 2

P1

1

Page 53: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

53

1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.3. Добавить каждому полученного числу ранг соответствующего процесса4. Отсортировать ранги процессов в соответствии с числами. 5. Отправить (scatter) процессам их порядковые номера.

P0 P2 P3

0 1 2 3

2023

2 1 3 0

3 1 0 2

P1

1

2. Сбор данных (gather)

3. Добавление рангов

4. Сортировка

5. Отправка (scatter) процессам

Page 54: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

54

// Собрать все числа на корневом процессеvoid *gather_numbers_to_root(void *number, MPI_Datatype datatype, MPI_Comm comm) { int commrank, commsize; MPI_Comm_rank(comm, &commrank); MPI_Comm_size(comm, &commsize);

// Выделить память на корневом процессе, учитывая тип массива number int datatype_size; MPI_Type_size(datatype, &datatype_size); void *gathered_numbers = NULL; if (commrank == 0) { gathered_numbers = malloc(datatype_size * commsize); }

// Собрать все числа на нулевом процессе в массиве gathered_numbers MPI_Gather(number, 1, datatype, gathered_numbers, 1, datatype, 0, comm);

return gathered_numbers;}

Page 55: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

55

typedef struct { int commrank; float number;} rank_t;

// Отсортировать ранги на коревом процессе и вернуть отсортированный массив ранговint *getranks(void *numbers, int numbers_count, MPI_Datatype type) { int i, typesize; MPI_Type_size(type, &typesize);

// Скопировать массив собранных чисел в массив типа rank_t rank_t *commrank_nums = malloc(numbers_count * sizeof(rank_t)); float *numbers_vals = (float *) numbers; for (i = 0; i < numbers_count; i++) { commrank_numbers[i].commrank = i; commrank_numbers[i].number = numbers_vals[i]; }

// Отсортировать ранги процессов, сравнивая полученные значения qsort(commrank_numbers, numbers_count, sizeof(rank_t), &cmp_ranks);

// Создать массив рангов и скопировать в него из ranks int *ranks = (int *)malloc(sizeof(int) * numbers_count); for (i = 0; i < numbers_count; i++) ranks[commrank_numbers[i].commrank] = i;

free(commrank_numbers); return ranks;}

Page 56: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

56

typedef struct { int commrank; float number;} rank_t;

// Отсортировать ранги на коревом процессе и вернуть отсортированный массив ранговint *getranks(void *numbers, int numbers_count, MPI_Datatype type) { int i, typesize; MPI_Type_size(type, &typesize);

// Скопировать массив собранных чисел в массив типа rank_t rank_t *commrank_nums = malloc(numbers_count * sizeof(rank_t)); float *numbers_vals = (float *) numbers; for (i = 0; i < numbers_count; i++) { commrank_numbers[i].commrank = i; commrank_numbers[i].number = numbers_vals[i * typesize]; }

// Отсортировать ранги процессов, сравнивая полученные значения qsort(commrank_numbers, numbers_count, sizeof(rank_t), &cmp_ranks);

// Создать массив рангов и скопировать в него из ranks int *ranks = (int *)malloc(sizeof(int) * numbers_count); for (i = 0; i < numbers_count; i++) ranks[commrank_numbers[i].commrank] = i;

free(commrank_numbers); return ranks;}

int cmp_ranks(const void *a, const void *b) { rank_t *commrank_number_a = (rank_t *)a; rank_t *commrank_number_b = (rank_t *)b;

if (commrank_number_a->number < commrank_number_b->number) { return -1; } else if (commrank_number_a->number > commrank_number_b->number) { return 1; } else { return 0; }}

Page 57: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

57

// Проранжировать процессы в соответствии с send_dataint rank_procs(void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm) { int commsize, commrank; MPI_Comm_size(comm, &commsize); MPI_Comm_rank(comm, &commrank);

// Собираем все числа void *numbers = gather_numbers_to_root(send_data, datatype, comm);

// Сортируем процессы в соответствии с числами int *ranks = NULL; if (comm_rank == 0) ranks = get_ranks(numbers, commsize, datatype);

// Отправляем каждому процессу его ранг MPI_Scatter(ranks, 1, MPI_INT, recv_data, 1, MPI_INT, 0, comm);

// Освободить память if (commrank == 0) { free(gathered_numbers); free(ranks); }}

Page 58: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

58

int main(int argc, char** argv) { MPI_Init(NULL, NULL);

int commrank, commsize; MPI_Comm_rank(MPI_COMM_WORLD, &commrank); MPI_Comm_size(MPI_COMM_WORLD, &commsize);

// Каждый процесс генерирует случайное число, которое будет использовать для ранжирования srand(time(NULL) * commrank); float rand_num = rand() / (float)RAND_MAX; int rank;

// Получить ранг процесса (rank) по случайному числу rand_num rank_procs(&rand_num, &rank, MPI_FLOAT, MPI_COMM_WORLD); printf("Rank for %f on process %d - %d\n", rand_num, commrank, rank);

MPI_Finalize(); return 0;}

Page 59: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: задача ранжирования процессов

59

int main(int argc, char** argv) { MPI_Init(NULL, NULL);

int commrank, commsize; MPI_Comm_rank(MPI_COMM_WORLD, &commrank); MPI_Comm_size(MPI_COMM_WORLD, &commsize);

// Каждый процесс генерирует случайное число, которое будет использовать для ранжирования srand(time(NULL) * commrank); float rand_num = rand() / (float)RAND_MAX; int rank;

// Получить ранг процесса (rank) по случайному числу rand_num rank_procs(&rand_num, &rank, MPI_FLOAT, MPI_COMM_WORLD); printf("Rank for %f on process %d - %d\n", rand_num, commrank, rank);

MPI_Finalize(); return 0;}

Rank for 0.840188 on process 0 - 7Rank for 0.567606 on process 1 - 5Rank for 0.453043 on process 2 - 2Rank for 0.201214 on process 4 - 1Rank for 0.542131 on process 3 - 4Rank for 0.727511 on process 5 - 6Rank for 0.464363 on process 6 - 3Rank for 0.194890 on process 7 - 0

Page 60: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

60

0

1

2

3

4

5

28

5

5

71

4

4

3

2

0 1 2 3 4 5

0 0 2 5 ∞ ∞ ∞

1 ∞ 0 7 1 ∞ 8

2 ∞ ∞ 0 4 ∞ ∞

3 ∞ ∞ ∞ 0 3 ∞

4 ∞ ∞ 2 ∞ 0 3

5 ∞ 5 ∞ 2 4 0

0 1 2 3 4 5

0 0 2 5 3 6 9

1 ∞ 0 6 1 4 7

2 ∞ 15 0 4 7 10

3 ∞ 11 5 0 3 6

4 ∞ 8 2 5 0 3

5 ∞ 5 6 2 4 0

d05 = 9

0 ➔ 1, 1 ➔ 3, 3 ➔ 4, 4 ➔ 5

Алгоритм Флойда поиска кратчайших путей в графе

Матрица смежности: Матрица кратчайших путей:

Page 61: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

61

Алгоритм Флойда – последовательный алгоритм

const int nvert = 10;

int main() { int *adjmatrix = NULL, *adjmatrix_chunk = NULL;

adjmatrix = generate_random_graph(nvert, argv[1]);

for (k = 0; k < nvert; k++) for (i = 0; i < nvert; i++) for (j = 0; j < nvert; j++) a[i * nvert + j] = MIN(a[i * nvert + j], a[i * nvert + k] + a[k * nvert + j]);

print_matrix_par(adjmatrix_chunk, nvert, nvert);

if (commrank == 0) free(adjmatrix); free(adjmatrix_chunk);

MPI_Finalize();}

Page 62: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

62

Алгоритм Флойда поиска кратчайших путей в графе

i

j

k

k

a[i][j] = MIN(a[i][j], a[i][k] + a[k][j])

i

k

j

P0

P1

P2

P3

Декомпозиция матрицы смежности:

Page 63: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

63

Алгоритм Флойда поиска кратчайших путей в графе

i

j

k

k

a[i][j] = MIN(a[i][j], a[i][k] + a[k][j])

i

k

j

P0

P1

P2

P3

Декомпозиция матрицы смежности:

k

k

Page 64: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

64

Алгоритм Флойда поиска кратчайших путей в графе

i

j

k

k

a[i][j] = MIN(a[i][j], a[i][k] + a[k][j])

i

k

j

P0

P1

P2

P3

Владелей k-й строки рассылает (MPI_Bcast) её остальным процессам

k

k

Page 65: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

65

Алгоритм Флойда поиска кратчайших путей в графе

const int nvert = 10;

int main() { int commrank, commsize; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &commrank); MPI_Comm_size(MPI_COMM_WORLD, &commsize);

int *adjmatrix = NULL, *adjmatrix_chunk = NULL;

if (commrank == 0) adjmatrix = generate_random_graph(nvert, argv[1]);

// Распределить матрицу adjmatrix_chunk = scatter_matrix_by_rows(adjmatrix, nvert, 0);

print_matrix_par(adjmatrix_chunk, nvert, nvert);

// Построить матрицу кратчайших путей compute_shortest_paths(commrank, commsize, adjmatrix_chunk, nvert);

print_matrix_par(adjmatrix_chunk, nvert, nvert);

if (commrank == 0) free(adjmatrix); free(adjmatrix_chunk);

MPI_Finalize();}

Page 66: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

66

// Вероятность наличия дуги в графеconst double ARC_EXISTENCE_PROBABILITY = 0.7;

// Функция генерации случайного графаint *generate_random_graph(int nvertices, const char *fname){ int i, j; int* adjmatrix = (int*) malloc(sizeof(int) * nvertices * nvertices);

for (i = 0; i < nvertices; i++) { for (j = 0; j < nvertices; j++) { if (drand48() < ARC_EXISTENCE_PROBABILITY) adjmatrix[i * nvertices + j] = lrand48() % MAX_WEIGHT; else adjmatrix[i * nvertices + j] = INF; } }

return adjmatrix;}

Алгоритм Флойда поиска кратчайших путей в графе

Page 67: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

67

// Функция распределения матрицы по строкамint *scatter_matrix_by_rows(int *adjmatrix, int nvertices, int root){ int commsize; MPI_Comm_size(MPI_COMM_WORLD, &commsize); int sendcount = nvertices * nvertices / commsize; int* adjmatrix_chunk = (int*) malloc(sizeof(int) * nvertices * nvertices);

MPI_Scatter(adjmatrix, sendcount, MPI_INT, adjmatrix_chunk, sendcount, MPI_INT, 0, MPI_COMM_WORLD);

return adjmatrix_chunk;}

Алгоритм Флойда поиска кратчайших путей в графе

Page 68: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

68

void compute_shortest_paths(int commrank, int commsize, int *adjmatrix, int nvertices) { int i, j, k; int offset; // Локальный индекс строки для рассылки int root; // Процесс, владеющий рассылаемой строкой int* tmp; // Массив для хранения рассылаемой строки

tmp = (int *) malloc (nvertices * sizeof(int)); for (k = 0; k < nvertices; k++) { root = chunk_owner(k, commsize, nvertices); if (root == commrank) { offset = k - chunk_low(commrank, commsize, nvertices); // Скопировать строку во временный массив for (j = 0; j < nvertices; j++) tmp[j] = adjmatrix[offset * nvertices + j]; }

// Разослать строку всем процессам MPI_Bcast(tmp, nvertices, MPI_TYPE, root, MPI_COMM_WORLD);

// Выполнить вычисления по алгоритму Флойда for (i = 0; i < chunk_size(commrank, commsize, nvertices); i++) for (j = 0; j < nvertices; j++) adjmatrix[i * nvertices + j] = min(adjmatrix[i * nvertices + j], adjmatrix[i * nvertices + k] + tmp[j]); // tmp[j] == a[k][j] } free (tmp);}

Алгоритм Флойда поиска кратчайших путей в графе

Page 69: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

69

#define INF -1 // Отсутствие дуги между вершинами

inline int min(int a, int b) { if ((a == INF) && (b == INF)) return a; else if (a == INF) return b; else if (b == INF) return a; else return a < b ? a : b;}

// Начало локальной области (полосе матрицы) процесса#define chunk_low(commrank, commsize, nvert) \ ((commrank) * (nvert) / (commsize))

// Конец локальной области процесса#define chunk_high(commrank, commsize, nvert) \ (chunk_low((commrank) + 1, commsize, nvert) - 1)

// Количество элементов в локальной области процесса#define chunk_size(commrank, commsize, nvert) \ (chunk_high(commrank, commsize, nvert) - \ chunk_low(commrank, commsize, nvert) + 1)

// Номер процесса, владеющий элементом#define chunk_owner(j, commsize, nvert) \ (((commsize) * ((j) + 1) - 1) / (nvert))

Алгоритм Флойда поиска кратчайших путей в графе

Page 70: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Пример: умножение матрицы на вектор

70

P1P0

a1

P2 P3

До вычислений

После вычислений

a

xx1

a2

x2

a3

x3

a4

x4

P1P0 P2 P3

Матрица а размерности m × n и вектор x длины n распределены между узлами.

Необходимо посчитать вектор y = a x, элементы которого распределены между узлами.

a

x

a1

x1

a2

x2

a3

x3

a4

x4

y1 y2 y3 y4y

Page 71: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

Умножение матрицы на вектор: 1-й вариант

71

P0 P2 P3P1

a0 a1 a2 a3

В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам.

x0 x1 x2 x3

Page 72: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

72

P0 P2 P3P1

a0

x0

a1

x1

a2

x2

a3

x3

В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам.1. Собрать в каждом процессе весь вектор x (MPI_Allgather).

Умножение матрицы на вектор: 1-й вариант

Page 73: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

73

P0 P2 P3P1

a0

x0

a1

x1

a2

x2

a3

x3

y0 y1 y2 y3

a0 × x0 a1 × x1 a2 × x2 a3 × x3

В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам.1. Собрать в каждом процессе весь вектор x (MPI_Allgather).

2. Выполнить вычисления на каждом процессе и получить часть результирующего вектора y.

Умножение матрицы на вектор: 1-й вариант

Page 74: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

74

MPI_Init(NULL, NULL);MPI_Comm_rank(MPI_COMM_WORLD, &worldrank);MPI_Comm_size(MPI_COMM_WORLD, &worldsize);

srand48(time(NULL) * worldrank);

// Выделить память под фраменты матрицы и вектора в каждом процессеdouble *a = (double*) malloc(sizeof(double) * (n * (m / worldsize)));double *x = (double*) malloc(sizeof(double) * n / worldsize);double *y = (double*) malloc(sizeof(double) * m / worldsize);

fill_matrix_randomly(a, n, m / worldsize);fill_matrix_randomly(x, n / worldsize, 1);

// Запустить параллельный алгоритм и измерить времяdouble t1 = MPI_Wtime();

matrix_vector_multiply_par1(a, x, y, n, m);

MPI_Barrier(MPI_COMM_WORLD);if (worldrank == 0) printf("\nParallel algorithm: %f s\n", MPI_Wtime() - t1);

// Распечатать результатыprint_results(a, x, y, n, m);

free(a); free(x); free(y);

MPI_Finalize();

Умножение матрицы на вектор: 1-й вариант

Page 75: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

75

MPI_Init(NULL, NULL);MPI_Comm_rank(MPI_COMM_WORLD, &worldrank);MPI_Comm_size(MPI_COMM_WORLD, &worldsize);

srand48(time(NULL) * worldrank);

// Выделить память под фраменты матрицы и вектора в каждом процессеdouble *a = (double*) malloc(sizeof(double) * (n * (m / worldsize)));double *x = (double*) malloc(sizeof(double) * n / worldsize);double *y = (double*) malloc(sizeof(double) * m / worldsize);

fill_matrix_randomly(a, n, m / worldsize);fill_matrix_randomly(x, n / worldsize, 1);

// Запустить параллельный алгоритм и измерить времяdouble t1 = MPI_Wtime();

matrix_vector_multiply_par1(a, x, y, n, m);

MPI_Barrier(MPI_COMM_WORLD);if (worldrank == 0) printf("\nParallel algorithm: %f s\n", MPI_Wtime() - t1);

// Распечатать результатыprint_results(a, x, y, n, m);

free(a); free(x); free(y);

MPI_Finalize();

Умножение матрицы на вектор: 1-й вариант

void fill_matrix_randomly(double *a, int n, int m) { int i, j; for (i = 0; i < m; i++) for (j = 0; j < n; j++) a[i * n + j] = lrand48() % 100; }

Page 76: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

76

void matrix_vector_multiply_par1(double *a, double *x, double *y, int n, int m){ // Выделить память под весь вектор x double *x_full = (double*) malloc(sizeof(double) * n);

// Собрать на всех процессах весь вектор x MPI_Allgather(x, n / worldsize, MPI_DOUBLE, x_full, n / worldsize, MPI_DOUBLE, MPI_COMM_WORLD);

// Рассчитать часть результирующего вектора y int i, j; for (i = 0; i < m / worldsize; i++) { y[i] = 0; for (j = 0; j < n; j++) y[i] += a[i * n + j] * x_full[j]; }

free(x_full);}

Умножение матрицы на вектор: 1-й вариант

Page 77: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

77

Serial algorithm:

matrix: 14 40 54 41 40 58 25 34 14 40 96 31 31 92 34 18 39 45 32 61 10 85 65 21 98 17 70 4 15 93 47 34 99 57 91 86 93 23 82 11 27 9 49 20 68 57 23 99 88 66 97 62 31 82 86 90 43 42 63 17 20 34 12 47 43 21 35 2 33 27 2 71 75 13 57 0 36 45 91 87 8 90 25 42 3 70 90 86 43 22 11 45 32 98 24 95 57 86 2 82 80 36 17 40 82 73 63 85 63 45 69 71 43 10 72 82 25 63 77 19 14 51 82 14 14 75 10 52

vector x: 99 56 28 69 76 81 39 70

vector y:19060 20847 23136 25776 34374 23541 38054 17578 16294 25769 24858 26858 28632 35929 23827 18673

Умножение матрицы на вектор: 1-й вариант

Page 78: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

78

Process 0:

matrix: 14 40 54 41 40 58 25 34 14 40 96 31 31 92 34 18 39 45 32 61 10 85 65 21 98 17 70 4 15 93 47 34

vector x: 99 56

vector y:19060 20847 23136 25776

Process 1:

matrix: 99 57 91 86 93 23 82 11 27 9 49 20 68 57 23 99 88 66 97 62 31 82 86 90 43 42 63 17 20 34 12 47

vector x: 28 69

vector y:34374 23541 38054 17578

...

Умножение матрицы на вектор: 1-й вариант

Page 79: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

79

P0 P2 P3P1

a0 a1 a2 a3

x0 x1 x2 x3

Умножение матрицы на вектор: 2-й вариантЭлементы матрицы a распределены между процессами по столбцам.

Page 80: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

80

P0 P2 P3P1

a0 a1 a2 a3

x0 x1 x2 x3

Умножение матрицы на вектор: 2-й вариант

y0' y1' y2' y3'

a0 × x0 a1 × x1 a2 × x2 a3 × x3

Элементы матрицы a распределены между процессами по столбцам.1. Расчитать частичные суммы для вектора y по известным значениям а и x.

Page 81: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

81

P0 P2 P3P1

a0 a1 a2 a3

x0 x1 x2 x3

Умножение матрицы на вектор: 2-й вариант

y0' y1' y2' y3'

a0 × x0 a1 × x1 a2 × x2 a3 × x3

Элементы матрицы a распределены между процессами по столбцам.1. Расчитать частичные суммы для вектора y по известным значениям а и x.2. Вычислить суммы соответствующих элементов (MPI_Reduce)

Page 82: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

82

P0 P2 P3P1

a0 a1 a2 a3

x0 x1 x2 x3

Умножение матрицы на вектор: 2-й вариант

y0' y1' y2' y3'

a0 × x0 a1 × x1 a2 × x2 a3 × x3

y0 y1 y2 y3

Элементы матрицы a распределены между процессами по столбцам.1. Расчитать частичные суммы для вектора y по известным значениям а и x.2. Вычислить суммы соответствующих элементов (MPI_Reduce) и распределить

полученные значения между процессами (MPI_Scatter) ⇒ MPI_Reduce_scatter.

Page 83: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

83

MPI_Init(NULL, NULL);MPI_Comm_rank(MPI_COMM_WORLD, &worldrank);MPI_Comm_size(MPI_COMM_WORLD, &worldsize);

srand48(time(NULL) * worldrank);

// Выделить память под фраменты матрицы и вектора в каждом процессеdouble *a = (double*) malloc(sizeof(double) * (n / worldsize) * m);double *x = (double*) malloc(sizeof(double) * n / worldsize);double *y = (double*) malloc(sizeof(double) * m / worldsize);

// Хранить фрагмент матрицы транспонированнойfill_matrix_randomly(a, m, n / worldsize);fill_matrix_randomly(x, n / worldsize, 1);

// Запустить параллельный алгоритм и измерить времяdouble t1 = MPI_Wtime();

matrix_vector_multiply_par2(a, x, y, n, m);

MPI_Barrier(MPI_COMM_WORLD);if (worldrank == 0) printf("\nParallel algorithm: %f s\n", MPI_Wtime() - t1);

// Распечатать результатыprint_results(a, x, y, n, m);

free(a); free(x); free(y);

MPI_Finalize();

Умножение матрицы на вектор: 2-й вариант

Page 84: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

84

void matrix_vector_multiply_par2(double *a, double *x, double *y, int n, int m){ int i, j;

// Массив для хранения промежуточного результата (частичной суммы) double *y_tmp = (double*) malloc(sizeof(double) * m);

// Расчитать значения вектора y на основе имеющихся значений for (i = 0; i < m; i++) { y_tmp[i] = 0; for (j = 0; j < n / worldsize; j++) y_tmp[i] += a[j * m + i] * x[j]; }

// Сформировать массив элементов, которые отправляются на каждый процесс int *ycounts = (int*) malloc(sizeof(int) * worldsize); for (i = 0; i < worldsize; i++) ycounts[i] = m / worldsize;

// Просуммировать элементы вектора и распределить результат между узлами MPI_Reduce_scatter(y_tmp, y, ycounts, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

free(ycounts); free(y_tmp);}

Умножение матрицы на вектор: 2-й вариант

Page 85: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

85

Serial algorithm:

matrix: 14 39 87 52 19 36 21 87 40 45 73 58 53 22 34 34 54 32 67 69 35 17 78 84 41 61 22 90 74 98 90 66 40 10 61 39 69 71 84 53 58 85 35 22 51 50 72 83 25 65 98 42 86 2 65 45 34 21 91 2 83 10 84 55 14 98 39 83 3 23 22 34 40 17 9 86 13 10 25 31 96 70 85 7 25 47 15 62 31 4 36 81 80 73 81 58 31 15 72 64 44 68 75 94 92 93 61 2 1 86 5 31 34 47 39 56 71 12 65 94 18 34 23 59 87 19 95 88

vector x: 99 56 12 41 96 73 95 82

vector y:20327 22446 29670 39649 30984 34016 27280 26904 17590 15441 27071 32506 31418 24521 30337 32361

Умножение матрицы на вектор: 1-й вариант

Page 86: Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда

86

Process 0:

matrix: 14 40 54 41 40 58 25 34 14 40 96 31 31 92 34 18 39 45 32 61 10 85 65 21 98 17 70 4 15 93 47 34

vector x: 99 56

vector y:19060 20847 23136 25776

Process 1:

matrix: 87 73 67 22 61 35 98 91 39 9 85 36 72 61 39 23 52 58 69 90 39 22 42 2 83 86 7 81 64 2 56 59

vector x: 12 41

vector y:30984 34016 27280 26904

...

Умножение матрицы на вектор: 1-й вариант