Выводы.
В результате выполнения лабораторной работы изучены принципы формирования виртуальных топологий в MPI, влияние структуры графа на коммуникационные накладные расходы и масштабируемость параллельной программы, а также методы измерения и анализа времени выполнения.
В ходе работы реализована постановка заданной графовой топологии для чётного числа процессов, обмен целочисленными сообщениями между соседними процессами с последующей сортировкой и выводом полученных значений в порядке возрастания рангов отправителей, а также корректное завершение с освобождением ресурсов.
Были проведены измерения времени работы программы и построены график зависимости среднего времени от числа процессов и график замедления. Анализ графиков показал, что время выполнения растёт при увеличении числа процессов, так как доминируют накладные расходы на коммуникации, сетевая латентность и частичная сериализация из-за блокирующих операций, что усиливается дисбалансом нагрузки у чётных рангов и конкуренцией за ресурсы при использовании флага --oversubscribe.
В качестве формальной модели процесса обмена была нарисована сеть Петри, которая наглядно отражает состояния готовности процессов, синхронные пары переходов обмена и условие накопления всех приёмных токенов перед обработкой.
Приложение а Исходный код программы
Main.c
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
MPI_Init(&argc, &argv);
MPI_Comm world = MPI_COMM_WORLD;
int world_size, world_rank;
MPI_Comm_size(world, &world_size);
MPI_Comm_rank(world, &world_rank);
if (world_size % 2 != 0) {
if (world_rank == 0)
fprintf(stderr, "Error: number of processes must be even.\n");
MPI_Finalize();
return EXIT_FAILURE;
}
int K = world_size;
int *degree = (int*) calloc(K, sizeof(int));
int total_edges = 0;
for (int r = 0; r < K; ++r) {
int deg = 0;
if (r % 2 == 1) {
deg = 1;
} else {
if (r - 2 >= 0) deg++;
if (r + 2 < K) deg++;
if (r + 1 < K) deg++;
}
degree[r] = deg;
total_edges += deg;
}
int *index = (int*) malloc(sizeof(int) * K);
int cum = 0;
for (int i = 0; i < K; ++i) {
cum += degree[i];
index[i] = cum;
}
int *edges = (int*) malloc(sizeof(int) * total_edges);
int pos = 0;
for (int r = 0; r < K; ++r) {
if (r % 2 == 1) {
edges[pos++] = r - 1;
} else {
if (r - 2 >= 0) edges[pos++] = r - 2;
if (r + 2 < K) edges[pos++] = r + 2;
if (r + 1 < K) edges[pos++] = r + 1;
}
}
MPI_Comm graph_comm;
MPI_Graph_create(world, K, index, edges, 0, &graph_comm);
int my_rank;
MPI_Comm_rank(graph_comm, &my_rank);
int A = my_rank * 10 + 5;
int nbr_count;
MPI_Graph_neighbors_count(graph_comm, my_rank, &nbr_count);
int *nbrs = (int*) malloc(sizeof(int) * (nbr_count > 0 ? nbr_count : 1));
if (nbr_count > 0) {
MPI_Graph_neighbors(graph_comm, my_rank, nbr_count, nbrs);
}
int *recvd_vals = (int*) malloc(sizeof(int) * (nbr_count > 0 ? nbr_count : 1));
for (int i = 0; i < nbr_count; ++i) {
int partner = nbrs[i];
int recvbuf = -1;
MPI_Status status;
MPI_Sendrecv(&A, 1, MPI_INT, partner, 0, &recvbuf, 1, MPI_INT, partner, 0, graph_comm, &status);
recvd_vals[i] = recvbuf;
}
if (nbr_count > 0) {
int *ranks_arr = (int*) malloc(sizeof(int) * nbr_count);
int *vals_arr = (int*) malloc(sizeof(int) * nbr_count);
for (int i = 0; i < nbr_count; ++i) {
ranks_arr[i] = nbrs[i];
vals_arr[i] = recvd_vals[i];
}
for (int i = 0; i < nbr_count - 1; ++i) {
for (int j = 0; j < nbr_count - 1 - i; ++j) {
if (ranks_arr[j] > ranks_arr[j+1]) {
int tr = ranks_arr[j]; ranks_arr[j] = ranks_arr[j+1]; ranks_arr[j+1] = tr;
int tv = vals_arr[j]; vals_arr[j] = vals_arr[j+1]; vals_arr[j+1] = tv;
}
}
}
printf("Process = %d A = %d, from neighbors:", my_rank, A);
for (int i = 0; i < nbr_count; ++i) {
printf(" %d", vals_arr[i]);
}
printf("\n");
free(ranks_arr);
free(vals_arr);
} else {
printf("Process = %d A = %d, dont have neighbors.\n", my_rank, A);
}
free(nbrs);
free(recvd_vals);
MPI_Comm_free(&graph_comm);
free(degree);
free(index);
free(edges);
MPI_Finalize();
return EXIT_SUCCESS;
}
