From 9256e535ff344850470e823bc2b1abfc875a3bc2 Mon Sep 17 00:00:00 2001 From: Fabio Salvini Date: Sat, 19 Nov 2016 14:04:05 +0100 Subject: [PATCH] Changed project structure --- LICENSE | 2 +- Makefile | 26 ++++++++++ jacobi.conf | 16 ++++++ src/config.c | 58 +++++++++++++++++++++ src/config.h | 11 ++++ src/impl/mpi_line.c | 116 ++++++++++++++++++++++++++++++++++++++++++ src/impl/sequential.c | 50 ++++++++++++++++++ src/jacobi.h | 1 + src/main.c | 76 +++++++++++++++++++++++++++ src/utils.c | 56 ++++++++++++++++++++ src/utils.h | 46 +++++++++++++++++ 11 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 jacobi.conf create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/impl/mpi_line.c create mode 100644 src/impl/sequential.c create mode 100644 src/jacobi.h create mode 100644 src/main.c create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/LICENSE b/LICENSE index 472ac23..b57bd14 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ MIT License -Copyright (c) +Copyright (c) 2016 Fabio Salvini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f87372f --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CC=mpicc +CFLAGS=-Wall -lm -std=c99 +SRC=src +BUILD=build +BIN=bin + +all: sequential mpi_line + +sequential: config utils main + ${CC} ${CFLAGS} ${BUILD}/config.o ${BUILD}/utils.o ${BUILD}/main.o ${SRC}/impl/sequential.c -o ${BIN}/jacobi_sequential + +mpi_line: config utils main + ${CC} ${CFLAGS} ${BUILD}/config.o ${BUILD}/utils.o ${BUILD}/main.o ${SRC}/impl/mpi_line.c -o ${BIN}/jacobi_mpi_line + +main: ${SRC}/main.c + ${CC} -c ${CFLAGS} ${SRC}/main.c -o ${BUILD}/main.o + +config: ${SRC}/config.c + ${CC} -c ${CFLAGS} ${SRC}/config.c -o ${BUILD}/config.o + +utils: ${SRC}/utils.c + ${CC} -c ${CFLAGS} ${SRC}/utils.c -o ${BUILD}/utils.o + +.PHONY: clean +clean: + rm build/* bin/* diff --git a/jacobi.conf b/jacobi.conf new file mode 100644 index 0000000..3e7fd02 --- /dev/null +++ b/jacobi.conf @@ -0,0 +1,16 @@ +# Configuration file for the Jacobi project. + +# The size of the matrix (borders excluded). +N 5 + +# The value at each border. +NORTH 0.0 +EAST 0.0 +SOUTH 300.0 +WEST 0.0 + +# The initial value to assign at each internal cell. +INIT_VALUE 0.0 + +# The threshold that determines the convergence. +THRESHOLD 1.0 diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..7d1c1b7 --- /dev/null +++ b/src/config.c @@ -0,0 +1,58 @@ +#include +#include + +typedef struct configuration { + int n; + double north; + double east; + double south; + double west; + double init_value; + double threshold; +} configuration; + +int load_config(configuration *config) { + char property[100]; + char *value; + FILE *fp; + + fp = fopen("jacobi.conf", "r"); + if (fp == NULL) { + perror("Error opening file jacobi.conf"); + return 1; + } + while (fgets(property, 100, fp) != NULL) { + if (property[0] == '\n' || property[0] == '#') { + /* Skip empty lines and comments */ + continue; + } + value = strchr(property, ' '); + if (value == NULL) { + fclose(fp); + perror("Error reading file jacobi.conf"); + return 1; + } + value[0] = '\0'; + value += sizeof(char); + value[strlen(value) - 1] = '\0'; + if (strcmp(property, "N") == 0) { + sscanf(value, "%d", &(config->n)); + } else if (strcmp(property, "NORTH") == 0) { + sscanf(value, "%lf", &(config->north)); + } else if (strcmp(property, "EAST") == 0) { + sscanf(value, "%lf", &(config->east)); + } else if (strcmp(property, "SOUTH") == 0) { + sscanf(value, "%lf", &(config->south)); + } else if (strcmp(property, "WEST") == 0) { + sscanf(value, "%lf", &(config->west)); + } else if (strcmp(property, "INIT_VALUE") == 0) { + sscanf(value, "%lf", &(config->init_value)); + } else if (strcmp(property, "THRESHOLD") == 0) { + sscanf(value, "%lf", &(config->threshold)); + } else { + printf("Unknown property %s\n", property); + } + } + fclose(fp); + return 0; +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..32635dd --- /dev/null +++ b/src/config.h @@ -0,0 +1,11 @@ +typedef struct configuration { + int n; + double north; + double east; + double south; + double west; + double init_value; + double threshold; +} configuration; + +int load_config(configuration *config); diff --git a/src/impl/mpi_line.c b/src/impl/mpi_line.c new file mode 100644 index 0000000..c9e9a79 --- /dev/null +++ b/src/impl/mpi_line.c @@ -0,0 +1,116 @@ +/* + * MPI version with the matrix subdivided by "lines". + */ + +#include +#include +#include +#include +#include "../config.h" +#include "../utils.h" + +#define TAG_BORDER 0 +#define TAG_MATRIX 1 + +double *compute_jacobi(int rank, int numprocs, int n, double init_value, double threshold, borders b, int *iterations) { + double *complete_x; + double *x; + double max_diff, global_max_diff, new_x; + int i, j; + int nb = n + 2; // n plus the border + int rows, rows_to_transmit; + int receive_pos; + MPI_Status status; + + if (rank == 0) { + rows = n - (n / numprocs) * (numprocs - 1); + } else { + rows = n / numprocs; + } + LOG(printf("[Process %d/%d] rows: %d\n", rank, numprocs, rows)); + /* LOG(printf("[Process %d/%d] initializing matrix\n", rank, numprocs)); */ + /* Initialize the matrix */ + x = create_sa_matrix(rows + 2, nb); + for (i = 0; i < rows + 2; i++) { + for (j = 1; j <= n; j++) { + x[IDX(nb, i, j)] = init_value; + } + } + /* Initialize boundary regions */ + for (i = 0; i < rows + 2; i++) { + x[IDX(nb, i, 0)] = b.west; + x[IDX(nb, i, n + 1)] = b.east; + } + if (rank == 0) { + for (i = 1; i <= n + 1; i++) { + x[IDX(nb, 0, i)] = b.north; + } + } + if (rank == numprocs - 1){ + for (i = 1; i < n + 1; i++) { + x[IDX(nb, rows + 1, i)] = b.south; + } + } + /* LOG(printf("[Process %d/%d] matrix initialized\n", rank, numprocs)); */ + /* Iterative refinement of x until values converge */ + *iterations = 0; + do { + max_diff = 0; + global_max_diff = 0; + for (i = 1; i <= rows; i++) { + for (j = 1; j <= n; j++) { + new_x = 0.25 * (x[IDX(nb, i - 1, j)] + x[IDX(nb, i, j + 1)] + x[IDX(nb, i + 1, j)] + x[IDX(nb, i, j - 1)]); + max_diff = (double) fmax(max_diff, fabs(new_x - x[IDX(nb, i, j)])); + x[IDX(nb, i, j)] = new_x; + } + } + if (rank % 2 == 0) { + if (rank != numprocs - 1) { + // Send and receive south border + MPI_Send(&x[IDX(nb, rows, 0)], nb, MPI_DOUBLE, rank + 1, TAG_BORDER, MPI_COMM_WORLD); + MPI_Recv(&x[IDX(nb, rows + 1, 0)], nb, MPI_DOUBLE, rank + 1, TAG_BORDER, MPI_COMM_WORLD, &status); + } + if (rank != 0) { + // Send and receive north border + MPI_Send(&x[IDX(nb, 1, 0)], nb, MPI_DOUBLE, rank - 1, TAG_BORDER, MPI_COMM_WORLD); + MPI_Recv(&x[IDX(nb, 0, 0)], nb, MPI_DOUBLE, rank - 1, TAG_BORDER, MPI_COMM_WORLD, &status); + } + } else { + // Receive and send north border + MPI_Recv(&x[IDX(nb, 0, 0)], nb, MPI_DOUBLE, rank - 1, TAG_BORDER, MPI_COMM_WORLD, &status); + MPI_Send(&x[IDX(nb, 1, 0)], nb, MPI_DOUBLE, rank - 1, TAG_BORDER, MPI_COMM_WORLD); + if (rank != numprocs - 1) { + // Receive and send south border + MPI_Recv(&x[IDX(nb, rows + 1, 0)], nb, MPI_DOUBLE, rank + 1, TAG_BORDER, MPI_COMM_WORLD, &status); + MPI_Send(&x[IDX(nb, rows, 0)], nb, MPI_DOUBLE, rank + 1, TAG_BORDER, MPI_COMM_WORLD); + } + } + /* LOG(printf("[Process %d/%d] max_diff: %f\n", rank, numprocs, max_diff)); */ + MPI_Allreduce(&max_diff, &global_max_diff, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + /* LOG(printf("[Process %d/%d] global_max_diff: %f\n", rank, numprocs, global_max_diff)); */ + (*iterations)++; + } while (global_max_diff > threshold); + + if (rank == 0) { + complete_x = create_sa_matrix(nb, nb); + memcpy(complete_x, x, (rows + ((rank == numprocs - 1) ? 2 : 1)) * (nb) * sizeof(double)); + rows_to_transmit = n / numprocs; + receive_pos = rows + 1; + for (i = 1; i < numprocs; i++) { + if (i == numprocs - 1) { + rows_to_transmit++; + } + MPI_Recv(&complete_x[IDX(nb, receive_pos, 0)], rows_to_transmit * (nb), MPI_DOUBLE, i, TAG_MATRIX, MPI_COMM_WORLD, &status); + receive_pos += n / numprocs; + } + } else { + complete_x = NULL; + rows_to_transmit = rows; + if (rank == numprocs - 1) { + rows_to_transmit++; + } + MPI_Send(&x[IDX(nb, 1, 0)], rows_to_transmit * (nb), MPI_DOUBLE, 0, TAG_MATRIX, MPI_COMM_WORLD); + } + + return complete_x; +} diff --git a/src/impl/sequential.c b/src/impl/sequential.c new file mode 100644 index 0000000..441fc96 --- /dev/null +++ b/src/impl/sequential.c @@ -0,0 +1,50 @@ +/* + * Sequential version. + * No paralleliization used. + */ + +#include +#include +#include +#include "../config.h" +#include "../utils.h" + +double *compute_jacobi(int rank, int numprocs, int n, double init_value, double threshold, borders b, int *iterations) { + double *x; + double max_diff, new_x; + int i, j; + int nb = n + 2; // n plus the border + + if (numprocs != 1) { + MPI_Abort(MPI_COMM_WORLD, 1); + } + + /* Initialize boundary regions */ + x = create_sa_matrix(n + 2, n + 2); + for (i = 1; i <= n; i++) { + x[IDX(nb, 0, i)] = b.north; + x[IDX(nb, n + 1, i)] = b.south; + x[IDX(nb, i, 0)] = b.west; + x[IDX(nb, i, n + 1)] = b.east; + } + /* Initialize the rest of the matrix */ + for (i = 1; i <= n; i++) { + for (j = 1; j <= n; j++) { + x[IDX(nb, i, j)] = init_value; + } + } + /* Iterative refinement of x until values converge */ + *iterations = 0; + do { + max_diff = 0; + for (i = 1; i <= n; i++) { + for (j = 1; j <= n; j++) { + new_x = 0.25 * (x[IDX(nb, i - 1, j)] + x[IDX(nb, i, j + 1)] + x[IDX(nb, i + 1, j)] + x[IDX(nb, i, j - 1)]); + max_diff = (double) fmax(max_diff, fabs(new_x - x[IDX(nb, i, j)])); + x[IDX(nb, i, j)] = new_x; + } + } + (*iterations)++; + } while (max_diff > threshold); + return x; +} diff --git a/src/jacobi.h b/src/jacobi.h new file mode 100644 index 0000000..74f34da --- /dev/null +++ b/src/jacobi.h @@ -0,0 +1 @@ +double *compute_jacobi(int rank, int numprocs, int n, double init_value, double threshold, borders b, int *iterations); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..8d410e8 --- /dev/null +++ b/src/main.c @@ -0,0 +1,76 @@ +/* + * MPI version with the matrix subdivided by "lines". + */ + +#include +#include +#include +#include +#include "config.h" +#include "utils.h" +#include "jacobi.h" + + +int main(int argc, char* argv[]) { + int rank; + int numprocs; + int n; + double init_value, threshold; + double north, south, east, west; + borders b; + int config_loaded; + configuration config; + double *x; + double startwtime = 0.0, endwtime; + int iterations; + + MPI_Init(&argc, &argv); + + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numprocs); + + if (rank == 0) { + config_loaded = load_config(&config); + if (config_loaded != 0) { + MPI_Abort(MPI_COMM_WORLD, 1); + } + n = config.n; + threshold = config.threshold; + init_value = config.init_value; + north = config.north; + south = config.south; + east = config.east; + west = config.west; + } + MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&init_value, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&threshold, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&north, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&south, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&east, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&west, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + b.north = north; + b.south = south; + b.east = east; + b.west = west; + + if (rank == 0) { + startwtime = MPI_Wtime(); + } + + x = compute_jacobi(rank, numprocs, n, init_value, threshold, b, &iterations); + + if (rank == 0) { + endwtime = MPI_Wtime(); + printf("Wall clock time: %fs\n", endwtime - startwtime); + printf("Iterations: %d\n", iterations); + print_sa_matrix(x, n + 2, n + 2); + } + + destroy_sa_matrix(x); + + MPI_Finalize(); + + return 0; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..7140bf8 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,56 @@ +#include +#include +#include "utils.h" + +double *create_sa_matrix(int rows, int cols) { + double *x; + + x = (double *) malloc(rows * cols * sizeof(double)); + return x; +} + +void destroy_sa_matrix(double *x) { + free(x); +} + +void print_sa_matrix(double *x, int rows, int cols) { + int i, j; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + printf("%f\t", x[IDX(cols, i, j)]); + } + printf("\n"); + } + fflush(stdout); +} + +double **create_matrix(int rows, int cols) { + int i; + double **x; + + x = (double **) malloc(rows * sizeof(double)); + for (i = 0; i < rows; i++) { + x[i] = (double *) malloc(cols * sizeof(double)); + } + return x; +} + +void destroy_matrix(double **x, int rows) { + int i; + + for (i = 0; i < rows; i++) { + free(x[i]); + } + free(x); +} + +void print_matrix(double **x, int rows, int cols) { + int i, j; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + printf("%f\t", x[i][j]); + } + printf("\n"); + } + fflush(stdout); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..eb0b37c --- /dev/null +++ b/src/utils.h @@ -0,0 +1,46 @@ +#include +#include + +/* #define ENABLE_LOG */ + +#ifdef ENABLE_LOG +# define LOG(x) x +#else +# define LOG(x) (void) 0 +#endif + +/* + * Macro used with single array matrices to + * get the array index given the number of columns, + * the row index and the column index. + */ +#define IDX(cols, r, c) ((r) * (cols) + (c)) + +typedef struct borders { + double north; + double east; + double south; + double west; +} borders; + + +/* + * Create a matrix stored in a single array. + */ +double *create_sa_matrix(int rows, int cols); + +/* + * Destroy a single array matrix. + */ +void destroy_sa_matrix(double *x); + +int sa_index(int cols, int r, int c); + +/* + * Print a single array matrix. + */ +void print_sa_matrix(double *x, int rows, int cols); + +double **create_matrix(int rows, int cols); +void destroy_matrix(double **x, int rows); +void print_matrix(double **x, int rows, int cols);