Upload
alexey-paznikov
View
1.161
Download
5
Embed Size (px)
Citation preview
Лекция 2. Коллективные операции в стандарте MPI
Пазников Алексей Александрович
Параллельные вычислительные технологии СибГУТИ (Новосибирск) Осенний семестр 2015
www: cpct.sibsutis.ru/~apaznikov/teaching q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall
2
Коллективные операции
▪ Коллективные коммуникации – это коммуникационные операции, в которых одновременно участвуют все процессы одного коммуникатора.
▪ Коллективные операции – это высокоуровневые операции, по сравнению с дифференцированными обменами (поскольку, как правило, реализованы на основе дифференцированных обменов).
Коммуникатор
P0P1
P2
P3
P4
P5
P6
3
Коллективные операции
▪ Коллективные коммуникации – это коммуникационные операции, в которых одновременно участвуют все процессы одного коммуникатора.
▪ Коллективные операции – это высокоуровневые операции, по сравнению с дифференцированными обменами (поскольку, как правило, реализованы на основе дифференцированных обменов).
Коммуникатор
▪ Коллективные операции бывают блокирующими и неблокирующими.
▪ В коллективных операциях отсутствуют тэги.
▪ Буфер получения должен иметь в точности необходимый размер.
P0P1
P2
P3
P4
P5
P6
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
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
Пример: вычисление числа пи
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
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)
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)
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)); // ...}
Пример: вычисление числа пи
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
Пример: вычисление числа пи
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
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)); // ...
Пример: вычисление числа пи
Пример: вычисление числа пи
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
14
P1P0 P2 P4
До вычисления среднего арифметического
После вычисления среднего арифметического
array
Корневой процесс генерирует случайный массив вещественных чисел. Необходимо расчитать среднее арифметическое этих чисел и сохранить результат в корневом процессе.
Пример: вычисление среднего арифметического чисел
P1P0 P2 P4
average
array
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
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
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)
Пример: вычисление среднего арифметического чисел
18
P0 P1 P2 P4
1. Генерация случайного массива чисел на корневом (нулевом) процессе.
Пример: вычисление среднего арифметического чисел
19
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.
P0 P1 P2 P4
Пример: вычисление среднего арифметического чисел
20
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.
P0 P1 P2 P4
Пример: вычисление среднего арифметического чисел
21
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Сбор (gather) всех промежуточных средних значений на корневом процессе.
P0 P1 P2 P4
Пример: вычисление среднего арифметического чисел
22
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Сбор (gather) всех промежуточных средних значений на корневом процессе. 5. Вычисление корневым процессом финального среднего значения.
P0 P1 P2 P4
Пример: вычисление среднего арифметического чисел
23
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Сбор (gather) всех промежуточных средних значений на корневом процессе. 5. Вычисление корневым процессом финального среднего значения.
P0 P1 P2 P4
2. Scatter
4. Gather
1. Генерация
3. Вычисление
5. Вычисление
Пример: вычисление среднего арифметического чисел
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);}
Пример: вычисление среднего арифметического чисел
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);}
Генерируем исходный “глобальный” массив
Выделяем память под массив промежуточных результатов
Выделяем память под “локальный” массив
Пример: вычисление среднего арифметического чисел
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;}
Пример: вычисление среднего арифметического чисел
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
Пример: вычисление среднего арифметического чисел
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);
Пример: вычисление среднего арифметического чисел
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
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
31
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Редукция (reduce) всех промежуточных средних значений на корневом процессе. 5. Вычисление финального значения среднего арифметического на корневом процессе.
P0 P1 P2 P4
Вычисление среднего арифметического чисел с помощью MPI_Reduce
32
1. Генерация случайного массива чисел на корневом (нулевом) процессе.2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное
количество чисел.3. Каждый процесс рассчитывает среднее своего подмножества чисел.4. Редукция (reduce) всех промежуточных средних значений на корневом процессе. 5. Вычисление финального значения среднего арифметического на корневом процессе.
P0 P1 P2 P4
2. Scatter
4. Reduce
1. Генерация
3. Вычисление
Вычисление среднего арифметического чисел с помощью MPI_Reduce
5. Вычисление
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);
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
35
P1P0 P2 P4
До вычисления стандартного отклонения
После вычисления стандартного отклонения
array
Корневой процесс генерирует случайный массив вещественных чисел. Необходимо расчитать среднее арифметическое этих чисел и сохранить результат в корневом процессе.
Пример: вычисление стандартного отклонения
P1P0 P2 P4
standard deviation
array
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)
37
P0 P1 P2 P4
Пример: вычисление стандартного отклоненияЭлементы массива распределены между процессами.
38
1. Каждый процесс рассчитывает среднее своего подмножества чисел.
P0 P1 P2 P4
Пример: вычисление стандартного отклонения
39
1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах.
P0 P1 P2 P4
Пример: вычисление стандартного отклонения
40
1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.
P0 P1 P2 P4
Пример: вычисление стандартного отклонения
41
1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах.
P0 P1 P2 P4
Пример: вычисление стандартного отклонения
42
1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах.5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе.
P0 P1 P2 P4
Пример: вычисление стандартного отклонения
43
P0 P1 P2 P4
Пример: вычисление стандартного отклонения1. Каждый процесс рассчитывает среднее своего подмножества чисел.2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического.4. Вычисление локальной суммы значений xi – xaverage на всех процессах.5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе.6. Вычисление значения стандартного отклонения.
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. Вычисление промежуточной суммы
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);}
Пример: вычисление стандартного отклонения
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
Пример: задача ранжирования процессов
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.
Пример: задача ранжирования процессов
48
1. Сгенерировать случайное число на каждом процессе.
P0 P2 P3P1
Пример: задача ранжирования процессов
49
1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.
P0 P2 P3P1
Пример: задача ранжирования процессов
50
1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.3. Добавить каждому полученного числу ранг соответствующего процесса
P0 P2 P3
0 1 2 3
P1
Пример: задача ранжирования процессов
51
1. Сгенерировать случайное число на каждом процессе.2. Собрать (gather) все числа в нулевом процессе.3. Добавить каждому полученного числу ранг соответствующего процесса4. Отсортировать ранги процессов в соответствии с числами.
P0 P2 P3
0 1 2 3
2 1 3 0
P1
Пример: задача ранжирования процессов
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
Пример: задача ранжирования процессов
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) процессам
Пример: задача ранжирования процессов
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;}
Пример: задача ранжирования процессов
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;}
Пример: задача ранжирования процессов
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; }}
Пример: задача ранжирования процессов
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); }}
Пример: задача ранжирования процессов
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;}
Пример: задача ранжирования процессов
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
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
Алгоритм Флойда поиска кратчайших путей в графе
Матрица смежности: Матрица кратчайших путей:
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();}
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
Декомпозиция матрицы смежности:
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
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
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();}
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;}
Алгоритм Флойда поиска кратчайших путей в графе
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;}
Алгоритм Флойда поиска кратчайших путей в графе
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);}
Алгоритм Флойда поиска кратчайших путей в графе
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))
Алгоритм Флойда поиска кратчайших путей в графе
Пример: умножение матрицы на вектор
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
Умножение матрицы на вектор: 1-й вариант
71
P0 P2 P3P1
a0 a1 a2 a3
В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам.
x0 x1 x2 x3
72
P0 P2 P3P1
a0
x0
a1
x1
a2
x2
a3
x3
В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам.1. Собрать в каждом процессе весь вектор x (MPI_Allgather).
Умножение матрицы на вектор: 1-й вариант
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-й вариант
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-й вариант
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; }
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-й вариант
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-й вариант
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-й вариант
79
P0 P2 P3P1
a0 a1 a2 a3
x0 x1 x2 x3
Умножение матрицы на вектор: 2-й вариантЭлементы матрицы a распределены между процессами по столбцам.
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.
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)
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.
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-й вариант
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-й вариант
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-й вариант
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-й вариант