ЛЕКЦИЯ 2. Коллективные операции в стандарте MPI
Курс "Параллельные вычислительные технологии" (ПВТ), осень 2015
Сибирский государственный университет телекоммуникаций и информатики
Пазников Алексей Александрович
к.т.н., доцент кафедры вычислительных систем СибГУТИ
http://cpct.sibsutis.ru/~apaznikov
http://cpct.sibsutis.ru/~apaznikov/teaching
Лекция 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
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
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.
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
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-й вариант
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-й вариант