Добавил:
Tushkan
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:
// --- Настройки программы ---
// Индекс главного (управляющего) процесса
#define MAIN_PROC 0
// Ширина полосы матрицы B, передаваемая процессам
#define BANDWIDTH 100
// Число запусков теста
#define TEST_COUNT 5
// Путь к файлу логов
#define LOG_PATH "log-1000.txt"
// Отключение предупреждений безопасности
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <math.h>
#include <mpi.h>
int gen_random(float a, float b);
FILE* matrix_create(const char *path, int m, int n);
int write_result(const char *path, int *C, int m, int q);
void log_print(const char *message);
void print_errno(const char *message);
int main(int argc, char *argv[])
{
// Число процессов и номер текущего процесса
int procNum = 1;
int procRank = MAIN_PROC;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &procNum);
MPI_Comm_rank(MPI_COMM_WORLD, &procRank);
MPI_Status status;
// Время начала работы главного процесса
double startTime;
int i, j, k, p, h;
// Размерности матриц А{mxn}, B{nxq}, C{mxq}
int n = 1000, m = 1000, q = 1000;
// Пути к файлам исходных данных и результата
const char *A_path = "A.txt";
const char *B_path = "B.txt";
const char *C_path = "C.txt";
FILE *Astream;
FILE *Bstream;
FILE *Cstream;
FILE *Lstream;
double *results;
if(procRank == MAIN_PROC)
{
results = (double *)malloc(TEST_COUNT * sizeof(double));
Lstream = fopen(LOG_PATH, "a");
if(Lstream == NULL)
return 0;
fprintf(Lstream, "----------------------------------------------\n");
fprintf(Lstream, "Размерности матриц m=%d n=%d q=%d\n", m, n, q);
fprintf(Lstream, "Число процессоров %d\n", procNum);
fprintf(Lstream, "Ширина полосы %d\n", BANDWIDTH);
fclose(Lstream);
}
// Запускаем тест TEST_COUNT раз
for(h = 0; h < TEST_COUNT; h++)
{
MPI_Barrier(MPI_COMM_WORLD);
if(procRank == MAIN_PROC)
{
// Фиксируем время начала работы главного процесса
startTime = MPI_Wtime();
// Очищаем файл результата
Cstream = fopen(C_path, "w");
fclose(Cstream);
// Пытаемся открыть файлы с матрицами A и B
Astream = fopen(A_path, "r");
Bstream = fopen(B_path, "r");
// Обработка ошибок при открытии файлов
if(Astream == NULL || Bstream == NULL)
{
if((Astream != NULL && fclose(Astream))
|| (Bstream != NULL && fclose(Bstream)))
{
log_print("ERROR Не удалось закрыть потоки для матриц А и В");
MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER);
return 0;
}
// Генерация новых файлов с матрицами A и B
srand((unsigned int) time(NULL));
Astream = matrix_create(A_path, m, n);
Bstream = matrix_create(B_path, n, q);
if(Astream == NULL || Bstream == NULL)
return 0;
// Не учитываем время, затраченное на генерацию матриц
startTime = MPI_Wtime();
}
int n1;
if(fscanf(Astream, "%d %d\n", &m, &n) <= 0
|| fscanf(Bstream, "%d %d\n", &n1, &q) <= 0)
{
log_print("ERROR Не удалось считать размерности матриц");
MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER);
fclose(Astream);
fclose(Bstream);
return 0;
}
if(n != n1 || n <= 0 || m <= 0 || q <= 0 || n % BANDWIDTH)
{
if(n != n1)
log_print("ERROR Не верно заданы размерности матриц: Число столбцов в матрице A не совпадает с числом строк в матрице B");
if(n <= 0 || m <= 0 || q <= 0)
log_print("ERROR Не верно заданы размерности матриц: Размерность не может быть меньше либо равна нулю");
if(n % BANDWIDTH)
log_print("ERROR Недопустимая ширина полосы");
MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER);
fclose(Astream);
fclose(Bstream);
return 0;
}
}
// Рассылка всем исполнителям размерностей матриц
MPI_Bcast(&m, 1, MPI_INT, MAIN_PROC, MPI_COMM_WORLD);
MPI_Bcast(&n, 1, MPI_INT, MAIN_PROC, MPI_COMM_WORLD);
MPI_Bcast(&q, 1, MPI_INT, MAIN_PROC, MPI_COMM_WORLD);
// Число строк матрицы A, передаваемое на один процессор
int AprocRows = floor(((double) m) / ((double) procNum));
int BprocRows = BANDWIDTH;
int Asize = AprocRows * n;
int Bsize = BprocRows * q;
int Csize = AprocRows * q;
// Число элементов, обрабатываемых текущим процессором
int Arows = AprocRows;
int As = Asize;
int Bs = Bsize;
int Cs = Csize;
if(procRank == MAIN_PROC)
{
Arows = m - AprocRows * (procNum - 1);
As = Arows * n;
Cs = Arows * q;
}
// Выделение памяти
int *A = (int *)malloc(As * sizeof(int));
int *B = (int *)malloc(Bs * sizeof(int));
int *C = (int *)malloc(Cs * sizeof(int));
// Обработка ошибок выделения памяти
if(A == NULL || B == NULL || C == NULL)
{
print_errno("Не удалось выделить память");
MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER);
if(procRank == MAIN_PROC)
{
fclose(Astream);
fclose(Bstream);
}
if(A != NULL) free(A);
if(B != NULL) free(B);
if(C != NULL) free(C);
return 0;
}
// Чтение матрицы A и распределение её между исполнителями
if(procRank == MAIN_PROC)
{
for(p = 0; p < procNum; p++)
{
if(p != MAIN_PROC)
{
for(i = 0; i < Asize; i++)
fscanf(Astream, "%d", &A[i]);
MPI_Send(A, Asize, MPI_INT, p, 0, MPI_COMM_WORLD);
}
}
for(i = 0; i < As; i++)
fscanf(Astream, "%d", &A[i]);
fclose(Astream);
}
else
{
// Приём исполнителями своих фрагменов матрицы A
MPI_Recv(A, Asize, MPI_INT, MAIN_PROC, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
}
// Обнуление частей результирующей матрицы C
for(i = 0; i < Cs; i++)
C[i] = 0;
for(i = 0; i < n / BprocRows; i++)
{
// Чтение строк матрицы B главным процессом
if(procRank == MAIN_PROC)
{
for(j = 0; j < Bsize; j++)
fscanf(Bstream, "%d", &B[j]);
}
// Синхронизация процессов
MPI_Barrier(MPI_COMM_WORLD);
// Рассылка всем исполнителям строк матрицы B
MPI_Bcast(B, Bsize, MPI_INT, MAIN_PROC, MPI_COMM_WORLD);
// Вычисление частей результата
for(k = 0; k < BprocRows; k++)
{
for(p = 0; p < Arows; p++)
{
for(j = 0; j < q; j++)
{
C[p * q + j] += A[p * n + i * BprocRows + k] * B[k * q + j];
}
}
}
}
// Освобождаем память
free(A);
free(B);
if(procRank == MAIN_PROC)
fclose(Bstream);
// Сбор частей результирующей матрицы со всех процессов на главном процессе
if(procRank != MAIN_PROC)
{
MPI_Send(C, Csize, MPI_INT, MAIN_PROC, 0, MPI_COMM_WORLD);
}
else
{
// Сохраняем часть результата, вычисленную на главном процессе
int *buf = (int *)malloc(Cs * sizeof(int));
if(buf == NULL)
{
print_errno("Не удалось выделить память для buf");
return 0;
}
for(i = 0; i < Cs; i++)
buf[i] = C[i];
for(p = 0; p < procNum; p++)
{
if(p != MAIN_PROC)
{
// Приём фрагментов результирующей матрицы на главном процессе
MPI_Recv(C, Csize, MPI_INT, p, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
if(write_result(C_path, C, AprocRows, q) == 0)
{
MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER);
if(buf != NULL) free(buf);
free(C);
return 0;
}
}
else if(write_result(C_path, buf, Arows, q) == 0)
{
MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER);
free(buf);
free(C);
return 0;
}
}
free(buf);
}
free(C);
// Записываем время работы главного процесса в лог
if(procRank == MAIN_PROC)
{
Lstream = fopen(LOG_PATH, "a");
if(Lstream == NULL)
return 0;
if(h == 0)
fprintf(Lstream, "Память %d МБ\n\n", round((Asize + Bsize + Csize) / 1024.0 / 1024.0 * sizeof(int)));
results[h] = MPI_Wtime() - startTime;
fprintf(Lstream, "Время (%d): %f сек\n", h + 1, results[h]);
fclose(Lstream);
}
}
if(procRank == MAIN_PROC)
{
Lstream = fopen(LOG_PATH, "a");
if(Lstream == NULL)
return 0;
int avg = 0;
for(i = 0; i < TEST_COUNT; i++)
avg += results[i];
free(results);
avg /= TEST_COUNT;
fprintf(Lstream, "Среднее : %f сек\n\n", avg);
fclose(Lstream);
}
MPI_Finalize();
return 0;
}
// Генерирует псевдо-случайное число из интервала
// a - левая граница интервала
// b - правая граница интервала
// Возвращает сгенерированное число из интервала [a;b]
int gen_random(float a, float b)
{
return a + (b - a) * ((float)rand() / RAND_MAX);
}
// Генерирует файл с матрицей m?n
// path - путь к файлу (например, имя файла или абсолютный путь к файлу)
// m - число строк в матрице
// n - число столбцов в матрице
// Возвращает указатель на открытый файл. Значение указателя, равное NULL, свидетельствует об ошибке.
FILE* matrix_create(const char *path, int m, int n)
{
if(n <= 0 || m <= 0)
{
log_print("Не удалось сгенерировать файл с матрицей: Не верно задана размерность матрицы");
return NULL;
}
FILE *stream = fopen(path, "w");
if(stream == NULL)
{
print_errno("Не удалось сгенерировать файл с матрицей");
return NULL;
}
if(fprintf(stream, "%d %d\n", m, n) < 0)
{
fclose(stream);
return NULL;
}
int i, j;
for(i = 0; i < m; i++)
{
for(j = 0; j < n - 1; j++)
{
if(fprintf(stream, "%d ", gen_random(-10,10)) < 0)
{
fclose(stream);
return NULL;
}
}
if(fprintf(stream, "%d\n", gen_random(-10,10)) < 0)
{
fclose(stream);
return NULL;
}
}
if(fclose(stream))
{
print_errno("Не удалось закрыть поток");
return NULL;
}
return fopen(path, "r");
}
// Дописывает часть результата в файл
// С - матрица данных части результата
// m - число строк
// q - число столбцов
// Возвращает 0 в случае ошибки, иначе 1.
int write_result(const char *path, int *C, int m, int q)
{
FILE *stream = fopen(path, "a");
if (stream == NULL)
{
print_errno("Не удалось создать файл для произведения матриц");
return 0;
}
int i, j;
for(i = 0; i < m; i++)
{
for(j = 0; j < q - 1; j++)
{
if(fprintf(stream, "%d ", C[i * q + j]) < 0)
{
fclose(stream);
return 0;
}
}
if(fprintf(stream, "%d\n", C[(i + 1) * q - 1]) < 0)
{
fclose(stream);
return 0;
}
}
if(fclose(stream))
{
print_errno("Не удалось закрыть поток");
return 0;
}
return 1;
}
// Запись сообщения в лог
// message - текстовое сообщение об ошибке
void log_print(const char *message)
{
FILE *stream = fopen(LOG_PATH, "a");
if(stream == NULL) return;
fprintf(stream, "%s\n", message);
fclose(stream);
}
// Выводит сообщение по коду ошибки errno
// message - необязательное дополнительное описание ошибки
void print_errno(const char *message)
{
char *msg = (char *)"ERROR ";
if(message != NULL)
msg = strcat(msg, message);
switch(errno)
{
case E2BIG : msg = strcat(msg, "Список аргументов слишком длинный"); break;
case EACCES : msg = strcat(msg, "Отказ в доступе"); break;
case EADDRINUSE : msg = strcat(msg, "Адрес используется"); break;
case EADDRNOTAVAIL : msg = strcat(msg, "Адрес недоступен"); break;
case EAFNOSUPPORT : msg = strcat(msg, "Семейство адресов не поддерживается"); break;
case EALREADY : msg = strcat(msg, "Соединение уже устанавливается"); break;
case EBADF : msg = strcat(msg, "Неправильный дескриптор файла"); break;
case EBADMSG : msg = strcat(msg, "Неправильное сообщение"); break;
case EBUSY : msg = strcat(msg, "Ресурс занят"); break;
case ECANCELED : msg = strcat(msg, "Операция отменена"); break;
case ECHILD : msg = strcat(msg, "Нет дочернего процесса"); break;
case ECONNABORTED : msg = strcat(msg, "Соединение прервано"); break;
case EDEADLK : msg = strcat(msg, "Обход тупика ресурсов"); break;
case EDESTADDRREQ : msg = strcat(msg, "Требуется адрес назначения"); break;
case EDOM : msg = strcat(msg, "Ошибка области определения"); break;
case EEXIST : msg = strcat(msg, "Файл существует"); break;
case EFAULT : msg = strcat(msg, "Неправильный адрес"); break;
case EFBIG : msg = strcat(msg, "Файл слишком велик"); break;
case EHOSTUNREACH : msg = strcat(msg, "Хост недоступен"); break;
case EIDRM : msg = strcat(msg, "Идентификатор удален"); break;
case EILSEQ : msg = strcat(msg, "Ошибочная последовательность байтов"); break;
case EINPROGRESS : msg = strcat(msg, "Операция в процессе выполнения"); break;
case EINTR : msg = strcat(msg, "Прерванный вызов функции"); break;
case EINVAL : msg = strcat(msg, "Неправильный аргумент"); break;
case EIO : msg = strcat(msg, "Ошибка ввода-вывода"); break;
case EISCONN : msg = strcat(msg, "Сокет (уже) соединен"); break;
case EISDIR : msg = strcat(msg, "Это каталог"); break;
case ELOOP : msg = strcat(msg, "Слишком много уровней символических ссылок"); break;
case EMFILE : msg = strcat(msg, "Слишком много открытых файлов"); break;
case EMLINK : msg = strcat(msg, "Слишком много связей"); break;
case EMSGSIZE : msg = strcat(msg, "Неопределённая длина буфера сообщения"); break;
case ENAMETOOLONG : msg = strcat(msg, "Имя файла слишком длинное"); break;
case ENETDOWN : msg = strcat(msg, "Сеть не работает"); break;
case ENETRESET : msg = strcat(msg, "Соединение прервано сетью"); break;
case ENETUNREACH : msg = strcat(msg, "Сеть недоступна"); break;
case ENFILE : msg = strcat(msg, "Слишком много открытых файлов в системе"); break;
case ENOBUFS : msg = strcat(msg, "Буферное пространство недоступно"); break;
case ENODEV : msg = strcat(msg, "Нет такого устройства"); break;
case ENOENT : msg = strcat(msg, "Нет такого файла в каталоге"); break;
case ENOEXEC : msg = strcat(msg, "Ошибка формата исполняемого файла"); break;
case ENOLCK : msg = strcat(msg, "Блокировка недоступна"); break;
case ENOLINK : msg = strcat(msg, "Зарезервировано"); break;
case ENOMEM : msg = strcat(msg, "Недостаточно памяти"); break;
case ENOMSG : msg = strcat(msg, "Сообщение нужного типа отсутствует"); break;
case ENOPROTOOPT : msg = strcat(msg, "Протокол недоступен"); break;
case ENOSPC : msg = strcat(msg, "Памяти на устройстве не осталось"); break;
case ENOSYS : msg = strcat(msg, "Функция не реализована"); break;
case ENOTCONN : msg = strcat(msg, "Сокет не соединен"); break;
case ENOTDIR : msg = strcat(msg, "Это не каталог"); break;
case ENOTEMPTY : msg = strcat(msg, "Каталог непустой"); break;
case ENOTSOCK : msg = strcat(msg, "Это не сокет"); break;
case ENOTTY : msg = strcat(msg, "Неопределённая операция управления вводом-выводом"); break;
case ENXIO : msg = strcat(msg, "Нет такого устройства или адреса"); break;
case EOPNOTSUPP : msg = strcat(msg, "Операция сокета не поддерживается"); break;
case EOVERFLOW : msg = strcat(msg, "Слишком большое значение для типа данных"); break;
case EPERM : msg = strcat(msg, "Операция не разрешена"); break;
case EPIPE : msg = strcat(msg, "Разрушенный канал"); break;
case EPROTO : msg = strcat(msg, "Ошибка протокола"); break;
case EPROTONOSUPPORT: msg = strcat(msg, "Протокол не поддерживается"); break;
case EPROTOTYPE : msg = strcat(msg, "Ошибочный тип протокола для сокета"); break;
case ERANGE : msg = strcat(msg, "Результат слишком велик"); break;
case EROFS : msg = strcat(msg, "Файловая система только на чтение"); break;
case ESPIPE : msg = strcat(msg, "Неправильное позиционирование"); break;
case ESRCH : msg = strcat(msg, "Нет такого процесса"); break;
case ETIMEDOUT : msg = strcat(msg, "Операция задержана"); break;
case ETXTBSY : msg = strcat(msg, "Текстовый файл занят"); break;
case EWOULDBLOCK : msg = strcat(msg, "Блокирующая операция"); break;
case EXDEV : msg = strcat(msg, "Неопределённая связь"); break;
default : msg = strcat(msg, "Неизвестная ошибка");
}
log_print(msg);
}