Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
па-пми / лр-6.docx
Скачиваний:
0
Добавлен:
10.06.2026
Размер:
198.85 Кб
Скачать

Выводы.

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

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

Были проведены измерения времени работы программы и построен график ускорения. Анализ графика показал, что для очень малых размеров матриц (m = 10, 50, 100) накладные расходы на распределение данных и обмены доминируют - ускорение мало или меньше единицы. При увеличении размера задачи выигрыши от распараллеливания становятся заметными: например, при m = 5000 последовательное время ≈237.67 с, время на 16 процессах ≈32.44 с (ускорение ≈7.72), на 64 процессах ≈31.00 с (ускорение ≈7.62), то есть при росте числа процессов ускорение растёт до некоторого предела и затем наступает насыщение. Для m = 1000 при увеличении числа процессов наблюдался явный рост ускорения при переходе от 4 к 16 процессам (примерно с ≈3.9 до ≈6.8). Такая картина согласуется с моделью, в которой вычислительная часть уменьшается при распараллеливании, а коммуникационная часть ограничивает дальнейшую масштабируемость.

В качестве формальной модели процесса обмена была построена сеть Петри, отражающая только основные этапы: распределение данных, начальное выравнивание блоков, последовательные вычислительные шаги и циклические сдвиги - модель используется для формального представления и анализа обменов между процессами.

Приложение а Исходный код программы

Main.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <math.h>

#include <mpi.h>

typedef long long val_t;

void seq_matmul(const val_t *A, const val_t *B, val_t *C, int m) {

for (int i = 0; i < m*m; ++i) C[i] = 0;

for (int i = 0; i < m; ++i) {

for (int k = 0; k < m; ++k) {

val_t a = A[i*m + k];

for (int j = 0; j < m; ++j) {

C[i*m + j] += a * B[k*m + j];

}

}

}

}

void local_block_mul_add(const val_t *A, const val_t *B, val_t *C, int bs) {

for (int i = 0; i < bs; ++i) {

for (int k = 0; k < bs; ++k) {

val_t a = A[i*bs + k];

for (int j = 0; j < bs; ++j) {

C[i*bs + j] += a * B[k*bs + j];

}

}

}

}

void *xmalloc(size_t s) {

void *p = malloc(s);

if (!p) { fprintf(stderr, "malloc failed\n"); MPI_Abort(MPI_COMM_WORLD, 1); }

return p;

}

int main(int argc, char **argv) {

MPI_Init(&argc, &argv);

int world_rank, world_size;

MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

MPI_Comm_size(MPI_COMM_WORLD, &world_size);

int m = 0;

val_t *A = NULL, *B = NULL, *Cseq = NULL;

if (world_rank == 0) {

if (scanf("%d", &m) != 1) {

fprintf(stderr, "Не удалось прочитать m\n");

MPI_Abort(MPI_COMM_WORLD, 1);

}

if (m <= 0) {

fprintf(stderr, "m должно быть > 0\n");

MPI_Abort(MPI_COMM_WORLD, 1);

}

A = (val_t*)xmalloc(sizeof(val_t) * m * m);

B = (val_t*)xmalloc(sizeof(val_t) * m * m);

for (int i = 0; i < m*m; ++i) {

if (scanf("%lld", &A[i]) != 1) { fprintf(stderr, "Bad input for A\n"); MPI_Abort(MPI_COMM_WORLD,1); }

}

for (int i = 0; i < m*m; ++i) {

if (scanf("%lld", &B[i]) != 1) { fprintf(stderr, "Bad input for B\n"); MPI_Abort(MPI_COMM_WORLD,1); }

}

}

MPI_Bcast(&m, 1, MPI_INT, 0, MPI_COMM_WORLD);

if (world_size == 1) {

if (world_rank == 0) {

Cseq = (val_t*)xmalloc(sizeof(val_t) * m * m);

double t0 = MPI_Wtime();

seq_matmul(A, B, Cseq, m);

double t1 = MPI_Wtime();

double serial_time = t1 - t0;

for (int i = 0; i < m; ++i) {

for (int j = 0; j < m; ++j) {

if (j) printf(" ");

printf("%lld", Cseq[i*m + j]);

}

printf("\n");

}

fprintf(stderr, "Serial time: %f s\n", serial_time);

free(A); free(B); free(Cseq);

}

MPI_Finalize();

return 0;

}

int q = (int)(floor(sqrt((double)world_size) + 0.5));

if (q * q != world_size) {

if (world_rank == 0) fprintf(stderr, "Число процессов (%d) не является полным квадратом\n", world_size);

MPI_Abort(MPI_COMM_WORLD, 2);

}

int reorder = 0;

int dims[2] = { q, q };

int periods[2] = { 1, 1 };

MPI_Comm comm2d;

MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, reorder, &comm2d);

int cart_rank;

MPI_Comm_rank(comm2d, &cart_rank);

int coords[2];

MPI_Cart_coords(comm2d, cart_rank, 2, coords);

int my_row = coords[0], my_col = coords[1];

int root_cart = -1;

if (world_rank == 0) root_cart = cart_rank;

MPI_Bcast(&root_cart, 1, MPI_INT, 0, MPI_COMM_WORLD);

int bs = (m + q - 1) / q;

int n = bs * q;

val_t *A_padded = NULL, *B_padded = NULL;

if (world_rank == 0) {

A_padded = (val_t*)calloc((size_t)n * n, sizeof(val_t));

B_padded = (val_t*)calloc((size_t)n * n, sizeof(val_t));

if (!A_padded || !B_padded) { fprintf(stderr, "calloc failed\n"); MPI_Abort(MPI_COMM_WORLD, 1); }

for (int i = 0; i < m; ++i)

for (int j = 0; j < m; ++j) {

A_padded[i*n + j] = A[i*m + j];

B_padded[i*n + j] = B[i*m + j];

}

}

val_t *localA = (val_t*)xmalloc(sizeof(val_t) * bs * bs);

val_t *localB = (val_t*)xmalloc(sizeof(val_t) * bs * bs);

val_t *localC = (val_t*)xmalloc(sizeof(val_t) * bs * bs);

for (int i = 0; i < bs*bs; ++i) localC[i] = 0;

if (world_rank == 0) {

for (int brow = 0; brow < q; ++brow) {

for (int bcol = 0; bcol < q; ++bcol) {

val_t *tmpA = (val_t*)xmalloc(sizeof(val_t) * bs * bs);

val_t *tmpB = (val_t*)xmalloc(sizeof(val_t) * bs * bs);

for (int i = 0; i < bs; ++i) {

for (int j = 0; j < bs; ++j) {

int gi = brow * bs + i;

int gj = bcol * bs + j;

if (gi < n && gj < n) {

tmpA[i*bs + j] = A_padded[gi*n + gj];

tmpB[i*bs + j] = B_padded[gi*n + gj];

} else {

tmpA[i*bs + j] = 0;

tmpB[i*bs + j] = 0;

}

}

}

int dest_coords[2] = {brow, bcol};

int dest_cart;

MPI_Cart_rank(comm2d, dest_coords, &dest_cart);

if (dest_cart == cart_rank && world_rank == 0) {

memcpy(localA, tmpA, sizeof(val_t)*bs*bs);

memcpy(localB, tmpB, sizeof(val_t)*bs*bs);

} else {

MPI_Send(tmpA, bs*bs, MPI_LONG_LONG, dest_cart, 100, comm2d);

MPI_Send(tmpB, bs*bs, MPI_LONG_LONG, dest_cart, 101, comm2d);

}

free(tmpA); free(tmpB);

}

}

} else {

MPI_Recv(localA, bs*bs, MPI_LONG_LONG, root_cart, 100, comm2d, MPI_STATUS_IGNORE);

MPI_Recv(localB, bs*bs, MPI_LONG_LONG, root_cart, 101, comm2d, MPI_STATUS_IGNORE);

}

MPI_Barrier(comm2d);

double t0 = MPI_Wtime();

MPI_Status status;

int src, dst;

MPI_Cart_shift(comm2d, 1, -my_row, &src, &dst);

MPI_Sendrecv_replace(localA, bs*bs, MPI_LONG_LONG, dst, 1, src, 1, comm2d, &status);

MPI_Cart_shift(comm2d, 0, -my_col, &src, &dst);

MPI_Sendrecv_replace(localB, bs*bs, MPI_LONG_LONG, dst, 2, src, 2, comm2d, &status);

for (int step = 0; step < q; ++step) {

local_block_mul_add(localA, localB, localC, bs);

MPI_Cart_shift(comm2d, 1, -1, &src, &dst);

MPI_Sendrecv_replace(localA, bs*bs, MPI_LONG_LONG, dst, 11, src, 11, comm2d, &status);

MPI_Cart_shift(comm2d, 0, -1, &src, &dst);

MPI_Sendrecv_replace(localB, bs*bs, MPI_LONG_LONG, dst, 12, src, 12, comm2d, &status);

}

double t1 = MPI_Wtime();

double local_elapsed = t1 - t0;

double max_elapsed;

MPI_Reduce(&local_elapsed, &max_elapsed, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);

if (world_rank == 0) {

val_t *C_padded = (val_t*)calloc((size_t)n * n, sizeof(val_t));

if (!C_padded) { fprintf(stderr, "calloc failed\n"); MPI_Abort(MPI_COMM_WORLD, 1); }

for (int i = 0; i < bs; ++i)

for (int j = 0; j < bs; ++j) {

int gi = my_row * bs + i, gj = my_col * bs + j;

C_padded[gi*n + gj] = localC[i*bs + j];

}

for (int brow = 0; brow < q; ++brow) {

for (int bcol = 0; bcol < q; ++bcol) {

int src_coords[2] = {brow, bcol};

int src_cart; MPI_Cart_rank(comm2d, src_coords, &src_cart);

if (src_cart == cart_rank && world_rank == 0) continue;

val_t *tmp = (val_t*)xmalloc(sizeof(val_t) * bs * bs);

MPI_Recv(tmp, bs*bs, MPI_LONG_LONG, src_cart, 200, comm2d, MPI_STATUS_IGNORE);

for (int i = 0; i < bs; ++i)

for (int j = 0; j < bs; ++j) {

int gi = brow*bs + i, gj = bcol*bs + j;

C_padded[gi*n + gj] = tmp[i*bs + j];

}

free(tmp);

}

}

for (int i = 0; i < m; ++i) {

for (int j = 0; j < m; ++j) {

if (j) printf(" ");

printf("%lld", C_padded[i*n + j]);

}

printf("\n");

}

fprintf(stderr, "Parallel time: %f s\n", max_elapsed);

free(C_padded);

} else {

MPI_Send(localC, bs*bs, MPI_LONG_LONG, root_cart, 200, comm2d);

}

free(localA); free(localB); free(localC);

if (world_rank == 0) { free(A_padded); free(B_padded); free(A); free(B); }

MPI_Comm_free(&comm2d);

MPI_Finalize();

return 0;

}

Соседние файлы в папке па-пми