SlideShare a Scribd company logo
1 of 86
Download to read offline
Лекция 2.
Коллективные операции
в стандарте MPI
Пазников Алексей Александрович
Параллельные вычислительные технологии
СибГУТИ (Новосибирск)
Осенний семестр 2015
www: cpct.sibsutis.ru/~apaznikov/teaching
q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall
2
Коллективные операции
▪ Коллективные коммуникации – это коммуникационные операции, в которых
одновременно участвуют все процессы одного коммуникатора.
▪ Коллективные операции – это высокоуровневые операции, по сравнению с
дифференцированными обменами (поскольку, как правило, реализованы на основе
дифференцированных обменов).
Коммуникатор
P0
P1
P2
P3
P4
P5
P6
3
Коллективные операции
▪ Коллективные коммуникации – это коммуникационные операции, в которых
одновременно участвуют все процессы одного коммуникатора.
▪ Коллективные операции – это высокоуровневые операции, по сравнению с
дифференцированными обменами (поскольку, как правило, реализованы на основе
дифференцированных обменов).
Коммуникатор
▪ Коллективные операции бывают блокирующими и неблокирующими.
▪ В коллективных операциях отсутствуют тэги.
▪ Буфер получения должен иметь в точности необходимый размер.
P0
P1
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 – di
2
).
w
sqrt(1–di
2
)
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 %.16fn",
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 %.16fn",
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 %dn", 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 %.16fn",
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 %dn", 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 %.16fn",
pi, fabs(pi - M_PI));
// ...process 0 generated n as 966321536
pi 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 до 1
float *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.500601
Avg of all elements from proc 4 is 0.500601
Avg of all elements from proc 1 is 0.500601
Avg of all elements from proc 5 is 0.500601
Avg of all elements from proc 2 is 0.500601
Avg of all elements from proc 6 is 0.500601
Avg of all elements from proc 3 is 0.500601
Avg 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 = %fn",
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 = %fn", 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 = %fn",
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 = %fn", global_sum, global_avg);
Local sum for process 3 - 499834.812500, avg = 0.499835
Local sum for process 1 - 500138.093750, avg = 0.500138
Local sum for process 5 - 500449.437500, avg = 0.500449
Local sum for process 4 - 500161.187500, avg = 0.500161
Local sum for process 7 - 499837.125000, avg = 0.499837
Local sum for process 6 - 498977.281250, avg = 0.498977
Local sum for process 2 - 500126.375000, avg = 0.500126
Local sum for process 0 - 500003.750000, avg = 0.500004
Total 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 = %fn", 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 = %fn", 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_data
int 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 - %dn", 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 - %dn", rand_num, commrank, rank);
MPI_Finalize();
return 0;
}
Rank for 0.840188 on process 0 - 7
Rank for 0.567606 on process 1 - 5
Rank for 0.453043 on process 2 - 2
Rank for 0.201214 on process 4 - 1
Rank for 0.542131 on process 3 - 4
Rank for 0.727511 on process 5 - 6
Rank for 0.464363 on process 6 - 3
Rank for 0.194890 on process 7 - 0
60
0
1
2
3
4
5
2
8
5
5
7
1
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 sn", 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 sn", 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 sn", 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-й вариант

More Related Content

What's hot

Лекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обменыЛекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обменыAlexey Paznikov
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksMikhail Kurnosov
 
Семинар 4. Многопоточное программирование на OpenMP (часть 4)
Семинар 4. Многопоточное программирование на OpenMP (часть 4)Семинар 4. Многопоточное программирование на OpenMP (часть 4)
Семинар 4. Многопоточное программирование на OpenMP (часть 4)Mikhail Kurnosov
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Mikhail Kurnosov
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPMikhail Kurnosov
 
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)Mikhail Kurnosov
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Yandex
 
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)Mikhail Kurnosov
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...Alexey Paznikov
 
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Yandex
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPMikhail Kurnosov
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...Alexey Paznikov
 
Семинар 9. Параллельное программирование на MPI (часть 2)
Семинар 9. Параллельное программирование на MPI (часть 2)Семинар 9. Параллельное программирование на MPI (часть 2)
Семинар 9. Параллельное программирование на MPI (часть 2)Mikhail Kurnosov
 
Семинар 3. Многопоточное программирование на OpenMP (часть 3)
Семинар 3. Многопоточное программирование на OpenMP (часть 3)Семинар 3. Многопоточное программирование на OpenMP (часть 3)
Семинар 3. Многопоточное программирование на OpenMP (часть 3)Mikhail Kurnosov
 
Семинар 7. Многопоточное программирование на OpenMP (часть 7)
Семинар 7. Многопоточное программирование на OpenMP (часть 7)Семинар 7. Многопоточное программирование на OpenMP (часть 7)
Семинар 7. Многопоточное программирование на OpenMP (часть 7)Mikhail Kurnosov
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...Alexey Paznikov
 
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияPlatonov Sergey
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияPlatonov Sergey
 

What's hot (20)

Лекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обменыЛекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
 
Семинар 4. Многопоточное программирование на OpenMP (часть 4)
Семинар 4. Многопоточное программирование на OpenMP (часть 4)Семинар 4. Многопоточное программирование на OpenMP (часть 4)
Семинар 4. Многопоточное программирование на OpenMP (часть 4)
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
 
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
 
Семинар 9. Параллельное программирование на MPI (часть 2)
Семинар 9. Параллельное программирование на MPI (часть 2)Семинар 9. Параллельное программирование на MPI (часть 2)
Семинар 9. Параллельное программирование на MPI (часть 2)
 
Семинар 3. Многопоточное программирование на OpenMP (часть 3)
Семинар 3. Многопоточное программирование на OpenMP (часть 3)Семинар 3. Многопоточное программирование на OpenMP (часть 3)
Семинар 3. Многопоточное программирование на OpenMP (часть 3)
 
Семинар 7. Многопоточное программирование на OpenMP (часть 7)
Семинар 7. Многопоточное программирование на OpenMP (часть 7)Семинар 7. Многопоточное программирование на OpenMP (часть 7)
Семинар 7. Многопоточное программирование на OpenMP (часть 7)
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
 
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализация
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
 

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

20140420 parallel programming_kalishenko_lecture10
20140420 parallel programming_kalishenko_lecture1020140420 parallel programming_kalishenko_lecture10
20140420 parallel programming_kalishenko_lecture10Computer Science Club
 
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)Mikhail Kurnosov
 
Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Mikhail Kurnosov
 
Стандарт MPI (Message Passing Interface)
Стандарт MPI (Message Passing Interface)Стандарт MPI (Message Passing Interface)
Стандарт MPI (Message Passing Interface)Mikhail Kurnosov
 
Функции передачи сообщений MPI
Функции передачи сообщений MPIФункции передачи сообщений MPI
Функции передачи сообщений MPIAleximos
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksMikhail Kurnosov
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1Michael Karpov
 
Optimization of Automata-Based Programs by means of Requirements Transformati...
Optimization of Automata-Based Programs by means of Requirements Transformati...Optimization of Automata-Based Programs by means of Requirements Transformati...
Optimization of Automata-Based Programs by means of Requirements Transformati...Iosif Itkin
 
трасировка Mpi приложений
трасировка Mpi приложенийтрасировка Mpi приложений
трасировка Mpi приложенийMichael Karpov
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMTech Talks @NSU
 
презентации продолжение банкета
презентации продолжение банкетапрезентации продолжение банкета
презентации продолжение банкетаstudent_kai
 
Лекция 1. Амортизационный анализ (Amortized analysis)
Лекция 1. Амортизационный анализ (Amortized analysis)Лекция 1. Амортизационный анализ (Amortized analysis)
Лекция 1. Амортизационный анализ (Amortized analysis)Mikhail Kurnosov
 
сбор урока
сбор урокасбор урока
сбор урокаLIANA180
 
Netmap (by luigi rizzo) простой и удобный opensource фреймворк для обработк...
Netmap (by luigi rizzo)   простой и удобный opensource фреймворк для обработк...Netmap (by luigi rizzo)   простой и удобный opensource фреймворк для обработк...
Netmap (by luigi rizzo) простой и удобный opensource фреймворк для обработк...Ontico
 
20090721 hpc exercise2
20090721 hpc exercise220090721 hpc exercise2
20090721 hpc exercise2Michael Karpov
 
Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Sergey Platonov
 

Similar to Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуждания и алгоритм Флойда поиска кратчайших путей в графе (20)

20140420 parallel programming_kalishenko_lecture10
20140420 parallel programming_kalishenko_lecture1020140420 parallel programming_kalishenko_lecture10
20140420 parallel programming_kalishenko_lecture10
 
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
 
Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)
 
Стандарт MPI (Message Passing Interface)
Стандарт MPI (Message Passing Interface)Стандарт MPI (Message Passing Interface)
Стандарт MPI (Message Passing Interface)
 
Функции передачи сообщений MPI
Функции передачи сообщений MPIФункции передачи сообщений MPI
Функции передачи сообщений MPI
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1
 
Optimization of Automata-Based Programs by means of Requirements Transformati...
Optimization of Automata-Based Programs by means of Requirements Transformati...Optimization of Automata-Based Programs by means of Requirements Transformati...
Optimization of Automata-Based Programs by means of Requirements Transformati...
 
трасировка Mpi приложений
трасировка Mpi приложенийтрасировка Mpi приложений
трасировка Mpi приложений
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
лекция 1
лекция 1лекция 1
лекция 1
 
презентации продолжение банкета
презентации продолжение банкетапрезентации продолжение банкета
презентации продолжение банкета
 
Лекция 1. Амортизационный анализ (Amortized analysis)
Лекция 1. Амортизационный анализ (Amortized analysis)Лекция 1. Амортизационный анализ (Amortized analysis)
Лекция 1. Амортизационный анализ (Amortized analysis)
 
Lektsia 9
Lektsia 9Lektsia 9
Lektsia 9
 
сбор урока
сбор урокасбор урока
сбор урока
 
Netmap (by luigi rizzo) простой и удобный opensource фреймворк для обработк...
Netmap (by luigi rizzo)   простой и удобный opensource фреймворк для обработк...Netmap (by luigi rizzo)   простой и удобный opensource фреймворк для обработк...
Netmap (by luigi rizzo) простой и удобный opensource фреймворк для обработк...
 
20090721 hpc exercise2
20090721 hpc exercise220090721 hpc exercise2
20090721 hpc exercise2
 
Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++
 
8 3-5
8 3-58 3-5
8 3-5
 

More from Alexey Paznikov

Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаAlexey Paznikov
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX ThreadsПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX ThreadsAlexey Paznikov
 
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюAlexey Paznikov
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияAlexey Paznikov
 
ПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаAlexey Paznikov
 
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 9
 ТФРВС - весна 2014 - лекция 9 ТФРВС - весна 2014 - лекция 9
ТФРВС - весна 2014 - лекция 9Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6Alexey Paznikov
 

More from Alexey Paznikov (16)

Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
 
ПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курса
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
 
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX ThreadsПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
 
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
 
ПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курса
 
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
 
ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11
 
ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10
 
ТФРВС - весна 2014 - лекция 9
 ТФРВС - весна 2014 - лекция 9 ТФРВС - весна 2014 - лекция 9
ТФРВС - весна 2014 - лекция 9
 
ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8
 
ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7
 
ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6
 

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

  • 1. Лекция 2. Коллективные операции в стандарте MPI Пазников Алексей Александрович Параллельные вычислительные технологии СибГУТИ (Новосибирск) Осенний семестр 2015 www: cpct.sibsutis.ru/~apaznikov/teaching q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall
  • 2. 2 Коллективные операции ▪ Коллективные коммуникации – это коммуникационные операции, в которых одновременно участвуют все процессы одного коммуникатора. ▪ Коллективные операции – это высокоуровневые операции, по сравнению с дифференцированными обменами (поскольку, как правило, реализованы на основе дифференцированных обменов). Коммуникатор P0 P1 P2 P3 P4 P5 P6
  • 3. 3 Коллективные операции ▪ Коллективные коммуникации – это коммуникационные операции, в которых одновременно участвуют все процессы одного коммуникатора. ▪ Коллективные операции – это высокоуровневые операции, по сравнению с дифференцированными обменами (поскольку, как правило, реализованы на основе дифференцированных обменов). Коммуникатор ▪ Коллективные операции бывают блокирующими и неблокирующими. ▪ В коллективных операциях отсутствуют тэги. ▪ Буфер получения должен иметь в точности необходимый размер. P0 P1 P2 P3 P4 P5 P6
  • 4. 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. 5 Вычисление числа пи методов численного интегрирования ▪ Разделить интервал на субинтервалы ▪ Распределить интервалы по процессам ▪ Каждый процесс рассчитывает частичную сумму ▪ Сложить все частичные суммы для вычисления числа пи 1 1 n сегментов 1. Каждый из сегментов имеет ширину w = 1 / n. 2. Расстояние для i-го сегмента от 0: di = i * w. 3. Высота i-го сегмента: sqrt(1 – di 2 ). w sqrt(1–di 2 ) di = i * w Пример: вычисление числа пи
  • 6. 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. 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. 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. 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 %.16fn", pi, fabs(pi - M_PI)); // ... } Пример: вычисление числа пи
  • 10. 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 %.16fn", pi, fabs(pi - M_PI)); // ... } pi is approximately 3.1415906524138242, Error is 0.0000020011759689 Пример: вычисление числа пи
  • 11. 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. 12 // ... // Сгенерировать в 0 процессе случайное число n if (rank == 0) { srand(time(NULL)); n = (rand() / (float) RAND_MAX) * MAX_N; printf("process 0 generated n as %dn", 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 %.16fn", pi, fabs(pi - M_PI)); // ... Пример: вычисление числа пи
  • 13. Пример: вычисление числа пи 13 // ... // Сгенерировать в 0 процессе случайное число n if (rank == 0) { srand(time(NULL)); n = (rand() / (float) RAND_MAX) * MAX_N; printf("process 0 generated n as %dn", 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 %.16fn", pi, fabs(pi - M_PI)); // ...process 0 generated n as 966321536 pi is approximately 3.1415926515201327, Error is 0.0000000020696604
  • 14. 14 P1P0 P2 P4 До вычисления среднего арифметического После вычисления среднего арифметического array Корневой процесс генерирует случайный массив вещественных чисел. Необходимо расчитать среднее арифметическое этих чисел и сохранить результат в корневом процессе. Пример: вычисление среднего арифметического чисел P1P0 P2 P4 average array
  • 15. 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
  • 16. 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
  • 17. 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. Пример: вычисление среднего арифметического чисел 18 P0 P1 P2 P4 1. Генерация случайного массива чисел на корневом (нулевом) процессе.
  • 19. Пример: вычисление среднего арифметического чисел 19 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. P0 P1 P2 P4
  • 20. Пример: вычисление среднего арифметического чисел 20 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. 3. Каждый процесс рассчитывает среднее своего подмножества чисел. P0 P1 P2 P4
  • 21. Пример: вычисление среднего арифметического чисел 21 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. 3. Каждый процесс рассчитывает среднее своего подмножества чисел. 4. Сбор (gather) всех промежуточных средних значений на корневом процессе. P0 P1 P2 P4
  • 22. Пример: вычисление среднего арифметического чисел 22 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. 3. Каждый процесс рассчитывает среднее своего подмножества чисел. 4. Сбор (gather) всех промежуточных средних значений на корневом процессе. 5. Вычисление корневым процессом финального среднего значения. P0 P1 P2 P4
  • 23. Пример: вычисление среднего арифметического чисел 23 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. 3. Каждый процесс рассчитывает среднее своего подмножества чисел. 4. Сбор (gather) всех промежуточных средних значений на корневом процессе. 5. Вычисление корневым процессом финального среднего значения. P0 P1 P2 P4 2. Scatter 4. Gather 1. Генерация 3. Вычисление 5. Вычисление
  • 24. Пример: вычисление среднего арифметического чисел 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. Пример: вычисление среднего арифметического чисел 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. Пример: вычисление среднего арифметического чисел 26 // Сгенерировать массив случайных чисел в диапазоне от 0 до 1 float *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. Пример: вычисление среднего арифметического чисел 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. Пример: вычисление среднего арифметического чисел 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. Пример: вычисление среднего арифметического чисел 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.500601 Avg of all elements from proc 4 is 0.500601 Avg of all elements from proc 1 is 0.500601 Avg of all elements from proc 5 is 0.500601 Avg of all elements from proc 2 is 0.500601 Avg of all elements from proc 6 is 0.500601 Avg of all elements from proc 3 is 0.500601 Avg of all elements from proc 7 is 0.500601
  • 30. 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. 31 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. 3. Каждый процесс рассчитывает среднее своего подмножества чисел. 4. Редукция (reduce) всех промежуточных средних значений на корневом процессе. 5. Вычисление финального значения среднего арифметического на корневом процессе. P0 P1 P2 P4 Вычисление среднего арифметического чисел с помощью MPI_Reduce
  • 32. 32 1. Генерация случайного массива чисел на корневом (нулевом) процессе. 2. Распределить (scatter) числа между всеми процессами, отправляя каждому процессу равное количество чисел. 3. Каждый процесс рассчитывает среднее своего подмножества чисел. 4. Редукция (reduce) всех промежуточных средних значений на корневом процессе. 5. Вычисление финального значения среднего арифметического на корневом процессе. P0 P1 P2 P4 2. Scatter 4. Reduce 1. Генерация 3. Вычисление Вычисление среднего арифметического чисел с помощью MPI_Reduce 5. Вычисление
  • 33. 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 = %fn", 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 = %fn", global_sum, global_avg);
  • 34. 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 = %fn", 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 = %fn", global_sum, global_avg); Local sum for process 3 - 499834.812500, avg = 0.499835 Local sum for process 1 - 500138.093750, avg = 0.500138 Local sum for process 5 - 500449.437500, avg = 0.500449 Local sum for process 4 - 500161.187500, avg = 0.500161 Local sum for process 7 - 499837.125000, avg = 0.499837 Local sum for process 6 - 498977.281250, avg = 0.498977 Local sum for process 2 - 500126.375000, avg = 0.500126 Local sum for process 0 - 500003.750000, avg = 0.500004 Total sum = 3999528.000000, avg = 0.499941
  • 35. 35 P1P0 P2 P4 До вычисления стандартного отклонения После вычисления стандартного отклонения array Корневой процесс генерирует случайный массив вещественных чисел. Необходимо расчитать среднее арифметическое этих чисел и сохранить результат в корневом процессе. Пример: вычисление стандартного отклонения P1P0 P2 P4 standard deviation array
  • 36. 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. 37 P0 P1 P2 P4 Пример: вычисление стандартного отклонения Элементы массива распределены между процессами.
  • 38. 38 1. Каждый процесс рассчитывает среднее своего подмножества чисел. P0 P1 P2 P4 Пример: вычисление стандартного отклонения
  • 39. 39 1. Каждый процесс рассчитывает среднее своего подмножества чисел. 2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. P0 P1 P2 P4 Пример: вычисление стандартного отклонения
  • 40. 40 1. Каждый процесс рассчитывает среднее своего подмножества чисел. 2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического. P0 P1 P2 P4 Пример: вычисление стандартного отклонения
  • 41. 41 1. Каждый процесс рассчитывает среднее своего подмножества чисел. 2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического. 4. Вычисление локальной суммы значений xi – xaverage на всех процессах. P0 P1 P2 P4 Пример: вычисление стандартного отклонения
  • 42. 42 1. Каждый процесс рассчитывает среднее своего подмножества чисел. 2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического. 4. Вычисление локальной суммы значений xi – xaverage на всех процессах. 5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе. P0 P1 P2 P4 Пример: вычисление стандартного отклонения
  • 43. 43 P0 P1 P2 P4 Пример: вычисление стандартного отклонения 1. Каждый процесс рассчитывает среднее своего подмножества чисел. 2. Редукция (all reduce) всех вычисленных средних значений на всех процессах. 3. Каждый процесс расчитывает значение среднего арифметического. 4. Вычисление локальной суммы значений xi – xaverage на всех процессах. 5. Редукция (reduce) для получения глобальной суммы xi – xaverage на корневом процессе. 6. Вычисление значения стандартного отклонения.
  • 44. 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. 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 = %fn", mean, stddev); } Пример: вычисление стандартного отклонения
  • 46. 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 = %fn", mean, stddev); } Пример: вычисление стандартного отклонения Mean - 0.499427, Standard deviation = 0.288920
  • 47. Пример: задача ранжирования процессов 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. Пример: задача ранжирования процессов 48 1. Сгенерировать случайное число на каждом процессе. P0 P2 P3P1
  • 49. Пример: задача ранжирования процессов 49 1. Сгенерировать случайное число на каждом процессе. 2. Собрать (gather) все числа в нулевом процессе. P0 P2 P3P1
  • 50. Пример: задача ранжирования процессов 50 1. Сгенерировать случайное число на каждом процессе. 2. Собрать (gather) все числа в нулевом процессе. 3. Добавить каждому полученного числу ранг соответствующего процесса P0 P2 P3 0 1 2 3 P1
  • 51. Пример: задача ранжирования процессов 51 1. Сгенерировать случайное число на каждом процессе. 2. Собрать (gather) все числа в нулевом процессе. 3. Добавить каждому полученного числу ранг соответствующего процесса 4. Отсортировать ранги процессов в соответствии с числами. P0 P2 P3 0 1 2 3 2 1 3 0 P1
  • 52. Пример: задача ранжирования процессов 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. Пример: задача ранжирования процессов 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. Пример: задача ранжирования процессов 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. Пример: задача ранжирования процессов 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. Пример: задача ранжирования процессов 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. Пример: задача ранжирования процессов 57 // Проранжировать процессы в соответствии с send_data int 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. Пример: задача ранжирования процессов 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 - %dn", rand_num, commrank, rank); MPI_Finalize(); return 0; }
  • 59. Пример: задача ранжирования процессов 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 - %dn", rand_num, commrank, rank); MPI_Finalize(); return 0; } Rank for 0.840188 on process 0 - 7 Rank for 0.567606 on process 1 - 5 Rank for 0.453043 on process 2 - 2 Rank for 0.201214 on process 4 - 1 Rank for 0.542131 on process 3 - 4 Rank for 0.727511 on process 5 - 6 Rank for 0.464363 on process 6 - 3 Rank for 0.194890 on process 7 - 0
  • 60. 60 0 1 2 3 4 5 2 8 5 5 7 1 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Пример: умножение матрицы на вектор 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
  • 71. Умножение матрицы на вектор: 1-й вариант 71 P0 P2 P3P1 a0 a1 a2 a3 В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам. x0 x1 x2 x3
  • 72. 72 P0 P2 P3P1 a0 x0 a1 x1 a2 x2 a3 x3 В каждом процессе находится часть матрицы a и вектора x. Причём элементы матрицы a распределены между процессами по строкам. 1. Собрать в каждом процессе весь вектор x (MPI_Allgather). Умножение матрицы на вектор: 1-й вариант
  • 73. 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. 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 sn", MPI_Wtime() - t1); // Распечатать результаты print_results(a, x, y, n, m); free(a); free(x); free(y); MPI_Finalize(); Умножение матрицы на вектор: 1-й вариант
  • 75. 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 sn", 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. 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. 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. 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. 79 P0 P2 P3P1 a0 a1 a2 a3 x0 x1 x2 x3 Умножение матрицы на вектор: 2-й вариант Элементы матрицы a распределены между процессами по столбцам.
  • 80. 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. 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. 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. 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 sn", MPI_Wtime() - t1); // Распечатать результаты print_results(a, x, y, n, m); free(a); free(x); free(y); MPI_Finalize(); Умножение матрицы на вектор: 2-й вариант
  • 84. 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. 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. 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-й вариант