/** * Author: Baptiste Coudray * School: HEPIA * Class: ITI-3 * Year 2020-2021 */ #include <stdio.h> #include <stdint.h> #include <mpi.h> #include <math.h> #include <stdbool.h> #include "../gol.h" #define INDEX_2D_TO_1D(y, x, nb_columns) ((y) * nb_columns + (x)) #define NORTH_INDEX 0 #define EAST_INDEX 1 #define SOUTH_INDEX 2 #define WEST_INDEX 3 #define NORTH_ROW_TAG 0 #define EAST_COLUMN_TAG 1 #define SOUTH_ROW_TAG 2 #define WEST_COLUMN_TAG 3 #define NORTH_EAST_CELL_TAG 4 #define SOUTH_EAST_CELL_TAG 5 #define SOUTH_WEST_CELL_TAG 6 #define NORTH_WEST_CELL_TAG 7 #define CHUNK_BOARD_TAG 8 #define ROOT_RANK 0 #define FPS 60 int createGridCommunicators(MPI_Comm *cartComm, MPI_Comm *rowComm, MPI_Comm *colComm, int nProc) { int gridN = (int) sqrt(nProc); int dimensions[2] = {gridN, gridN}; int periods[2] = {true, true}; // Cyclic on row-column MPI_Cart_create(MPI_COMM_WORLD, 2, dimensions, periods, 1, cartComm); /* Create row communicator */ int remainDims[2] = {false, true}; MPI_Cart_sub(*cartComm, remainDims, rowComm); /* Create column communicator */ remainDims[0] = true; // rows remainDims[1] = false; // columns MPI_Cart_sub(*cartComm, remainDims, colComm); return gridN; } int *divideBoard(int n, int chunkN, int nProc) { int *indexes = calloc((size_t) nProc * 2, sizeof(int)); for (int i = 0, y = 0, x = 0; i < nProc; ++i) { indexes[i * 2] = y; indexes[i * 2 + 1] = x; x += chunkN; if (x >= n) { x = 0; y += chunkN; } } return indexes; } void initChunkBoard(int8_t *chunkBoard, int chunkN) { for (int i = 0; i < chunkN; ++i) { for (int j = 0; j < chunkN; ++j) { chunkBoard[INDEX_2D_TO_1D(i, j, chunkN)] = rand() % 2; } } } void shareAndBuildEnvelope(int8_t *chunkBoardMyEnvelope, int8_t *chunkBoardEnvelope, MPI_Comm rowComm, MPI_Comm colComm, int gridN, int coordinates[2], int chunkN, int chunkM) { int coordinateY = coordinates[0]; int coordinateX = coordinates[1]; MPI_Request requests[16] = {0}; int iRequest = 0; // North { int8_t *chunkBoardMyEnvelopeNorth = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(NORTH_INDEX, 0, chunkN)]; int8_t *chunkBoardEnvelopeNorth = &chunkBoardEnvelope[INDEX_2D_TO_1D(NORTH_INDEX, 1, chunkM)]; int destSource = (coordinateY - 1) < 0 ? (gridN - 1) : (coordinateY - 1); MPI_Isend(chunkBoardMyEnvelopeNorth, chunkN, MPI_INT8_T, destSource, NORTH_ROW_TAG, colComm, &requests[iRequest++]); /* Neighbour send south row, which correspond to north envelope */ MPI_Irecv(chunkBoardEnvelopeNorth, chunkN, MPI_INT8_T, destSource, SOUTH_ROW_TAG, colComm, &requests[iRequest++]); } // East { int8_t *chunkBoardMyEnvelopeEast = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(EAST_INDEX, 0, chunkN)]; int8_t *chunkBoardEnvelopeEast = &chunkBoardEnvelope[INDEX_2D_TO_1D(EAST_INDEX, 1, chunkM)]; int destSource = (coordinateX + 1) % gridN; MPI_Isend(chunkBoardMyEnvelopeEast, chunkN, MPI_INT8_T, destSource, EAST_COLUMN_TAG, rowComm, &requests[iRequest++]); /* Neighbour send west column, which correspond to east envelope */ MPI_Irecv(chunkBoardEnvelopeEast, chunkN, MPI_INT8_T, destSource, WEST_COLUMN_TAG, rowComm, &requests[iRequest++]); } // South { int8_t *chunkBoardMyEnvelopeSouth = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(SOUTH_INDEX, 0, chunkN)]; int8_t *chunkBoardEnvelopeSouth = &chunkBoardEnvelope[INDEX_2D_TO_1D(SOUTH_INDEX, 1, chunkM)]; int destSource = (coordinateY + 1) % gridN; MPI_Isend(chunkBoardMyEnvelopeSouth, chunkN, MPI_INT8_T, destSource, SOUTH_ROW_TAG, colComm, &requests[iRequest++]); /* Neighbour send north row, which correspond to south envelope */ MPI_Irecv(chunkBoardEnvelopeSouth, chunkN, MPI_INT8_T, destSource, NORTH_ROW_TAG, colComm, &requests[iRequest++]); } // West { int8_t *chunkBoardMyEnvelopeWest = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(WEST_INDEX, 0, chunkN)]; int8_t *chunkBoardEnvelopeWest = &chunkBoardEnvelope[INDEX_2D_TO_1D(WEST_INDEX, 1, chunkM)]; int destSource = (coordinateX - 1) < 0 ? (gridN - 1) : (coordinateX - 1); MPI_Isend(chunkBoardMyEnvelopeWest, chunkN, MPI_INT8_T, destSource, WEST_COLUMN_TAG, rowComm, &requests[iRequest++]); /* Neighbour send east column, which correspond to west envelope */ MPI_Irecv(chunkBoardEnvelopeWest, chunkN, MPI_INT8_T, destSource, EAST_COLUMN_TAG, rowComm, &requests[iRequest++]); } int8_t missingCells[4] = {0}; // North-East { int8_t *chunkBoardMyEnvelopeNorthEast = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(NORTH_INDEX, chunkN - 1, chunkN)]; int destSrcY = (coordinateY - 1) < 0 ? gridN - 1 : coordinateY - 1; int destSrcX = (coordinateX + 1) % gridN; int destSource = INDEX_2D_TO_1D(destSrcY, destSrcX, gridN); MPI_Isend(chunkBoardMyEnvelopeNorthEast, 1, MPI_INT8_T, destSource, NORTH_EAST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); /* Neighbour send south-west cell, which correspond to north-east cell */ MPI_Irecv(&missingCells[1], 1, MPI_INT8_T, destSource, SOUTH_WEST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); } // South-East { int8_t *chunkBoardMyEnvelopeSouthEast = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(SOUTH_INDEX, chunkN - 1, chunkN)]; int destSrcY = (coordinateY + 1) % gridN; int destSrcX = (coordinateX + 1) % gridN; int destSource = INDEX_2D_TO_1D(destSrcY, destSrcX, gridN); MPI_Isend(chunkBoardMyEnvelopeSouthEast, 1, MPI_INT8_T, destSource, SOUTH_EAST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); /* Neighbour send north-west cell, which correspond to south-east cell */ MPI_Irecv(&missingCells[2], 1, MPI_INT8_T, destSource, NORTH_WEST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); } // South-West { int8_t *chunkBoardMyEnvelopeSouthWest = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(SOUTH_INDEX, 0, chunkN)]; int destSrcY = (coordinateY + 1) % gridN; int destSrcX = (coordinateX - 1) < 0 ? gridN - 1 : coordinateX - 1; int destSource = INDEX_2D_TO_1D(destSrcY, destSrcX, gridN); MPI_Isend(chunkBoardMyEnvelopeSouthWest, 1, MPI_INT8_T, destSource, SOUTH_WEST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); /* Neighbour send north-east cell, which correspond to south-west cell */ MPI_Irecv(&missingCells[3], 1, MPI_INT8_T, destSource, NORTH_EAST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); } // North-West { int8_t *chunkBoardMyEnvelopeNorthWest = &chunkBoardMyEnvelope[INDEX_2D_TO_1D(NORTH_INDEX, 0, chunkN)]; int destSrcY = (coordinateY - 1) < 0 ? gridN - 1 : coordinateY - 1; int destSrcX = (coordinateX - 1) < 0 ? gridN - 1 : coordinateX - 1; int destSource = INDEX_2D_TO_1D(destSrcY, destSrcX, gridN); MPI_Isend(chunkBoardMyEnvelopeNorthWest, 1, MPI_INT8_T, destSource, NORTH_WEST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest++]); /* Neighbour send south-east cell, which correspond to north-west cell */ MPI_Irecv(&missingCells[0], 1, MPI_INT8_T, destSource, SOUTH_EAST_CELL_TAG, MPI_COMM_WORLD, &requests[iRequest]); } MPI_Waitall(16, requests, MPI_STATUSES_IGNORE); /* Copy missing cells into the envelope */ chunkBoardEnvelope[INDEX_2D_TO_1D(NORTH_INDEX, chunkN, chunkM)] = chunkBoardEnvelope[INDEX_2D_TO_1D( EAST_INDEX, 0, chunkM)] = missingCells[1]; chunkBoardEnvelope[INDEX_2D_TO_1D(SOUTH_INDEX, chunkN, chunkM)] = chunkBoardEnvelope[INDEX_2D_TO_1D(EAST_INDEX, chunkN, chunkM)] = missingCells[2]; chunkBoardEnvelope[INDEX_2D_TO_1D(SOUTH_INDEX, 0, chunkM)] = chunkBoardEnvelope[INDEX_2D_TO_1D(WEST_INDEX, chunkN, chunkM)] = missingCells[3]; chunkBoardEnvelope[INDEX_2D_TO_1D(NORTH_INDEX, 0, chunkM)] = chunkBoardEnvelope[INDEX_2D_TO_1D(WEST_INDEX, 0, chunkM)] = missingCells[0]; } void chunkBoardToBoard(int8_t *board, int n, const int8_t *chunkBoard, int chunkN, const int *indexes, int rank) { int y = indexes[rank * 2]; int x = indexes[rank * 2 + 1]; for (int i = 0; i < chunkN; ++i) { for (int j = 0; j < chunkN; ++j) { board[INDEX_2D_TO_1D(y + i, x + j, n)] = chunkBoard[INDEX_2D_TO_1D(i, j, chunkN)]; } } } int main(int argc, char *argv[]) { if (argc < 3) { printf("%s <deviceName> <boardN>\n", argv[0]); exit(0); } int myRank; int nProc; double start = MPI_Wtime(); /* MPI Initialization */ MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); MPI_Comm_size(MPI_COMM_WORLD, &nProc); srand((unsigned int) myRank); MPI_Comm cartComm, rowComm, colComm; int gridN = createGridCommunicators(&cartComm, &rowComm, &colComm, nProc); int myCartRank; MPI_Comm_rank(cartComm, &myCartRank); int coordinates[2] = {0}; MPI_Cart_coords(cartComm, myCartRank, 2, coordinates); /* Futhark Initialization */ struct futhark_context_config *contextConfig = futhark_context_config_new(); int nbDevices = atoi(argv[1]); char device[3] = {0}; snprintf(device, sizeof(device), "#%d", myRank % nbDevices); futhark_context_config_set_device(contextConfig, device); struct futhark_context *futharkContext = futhark_context_new(contextConfig); /* GoL Initialization */ int boardN = atoi(argv[2]); int boardNN = boardN * boardN; int chunkN = (int) (boardN / sqrt(nProc)); int chunkNN = chunkN * chunkN; int chunkM = chunkN + 2; int *indexes = divideBoard(boardN, chunkN, nProc); int8_t *board = myRank == ROOT_RANK ? calloc((size_t) boardNN, sizeof(int8_t)) : NULL; int8_t *chunkBoard = calloc((size_t) chunkNN, sizeof(int8_t)); int8_t *chunkBoardMyEnvelope = calloc(((size_t) (4 * chunkN)), sizeof(int8_t)); int8_t *chunkBoardEnvelope = calloc(((size_t) (4 * chunkM)), sizeof(int8_t)); initChunkBoard(chunkBoard, chunkN); struct futhark_i8_2d *futChunkBoard = futhark_new_i8_2d(futharkContext, chunkBoard, chunkN, chunkN); futhark_context_sync(futharkContext); struct futhark_i8_2d *futChunkBoardMyEnvelope; futhark_entry_get_envelope(futharkContext, &futChunkBoardMyEnvelope, futChunkBoard); futhark_context_sync(futharkContext); futhark_values_i8_2d(futharkContext, futChunkBoardMyEnvelope, chunkBoardMyEnvelope); futhark_context_sync(futharkContext); shareAndBuildEnvelope(chunkBoardMyEnvelope, chunkBoardEnvelope, rowComm, colComm, gridN, coordinates, chunkN, chunkM); struct futhark_i8_2d *futChunkBoardEnvelope = futhark_new_i8_2d(futharkContext, chunkBoardEnvelope, 4, chunkM); futhark_context_sync(futharkContext); struct futhark_i8_2d *futNextChunkBoard; futhark_entry_next_chunk_board(futharkContext, &futNextChunkBoard, futChunkBoard, futChunkBoardEnvelope); futhark_context_sync(futharkContext); futhark_values_i8_2d(futharkContext, futNextChunkBoard, chunkBoard); futhark_context_sync(futharkContext); if (myRank == ROOT_RANK) { chunkBoardToBoard(board, boardN, chunkBoard, chunkN, indexes, myRank); int8_t *tmpChunkBoard = calloc((size_t) chunkNN, sizeof(int8_t)); MPI_Status status = {0}; for (int i = 0; i < nProc - 1; ++i) { MPI_Recv(tmpChunkBoard, chunkNN, MPI_INT8_T, MPI_ANY_SOURCE, CHUNK_BOARD_TAG, MPI_COMM_WORLD, &status); chunkBoardToBoard(board, boardN, tmpChunkBoard, chunkN, indexes, status.MPI_SOURCE); } free(tmpChunkBoard); } else { MPI_Send(chunkBoard, chunkNN, MPI_INT8_T, ROOT_RANK, CHUNK_BOARD_TAG, MPI_COMM_WORLD); } futhark_context_sync(futharkContext); futhark_free_i8_2d(futharkContext, futChunkBoard); futhark_free_i8_2d(futharkContext, futChunkBoardMyEnvelope); futhark_free_i8_2d(futharkContext, futChunkBoardEnvelope); futhark_free_i8_2d(futharkContext, futNextChunkBoard); free(indexes); free(chunkBoard); free(chunkBoardEnvelope); free(chunkBoardMyEnvelope); if (myRank == ROOT_RANK) { free(board); } futhark_context_free(futharkContext); futhark_context_config_free(contextConfig); MPI_Comm_free(&cartComm); MPI_Comm_free(&rowComm); MPI_Comm_free(&colComm); double finish = MPI_Wtime(); if (myRank == ROOT_RANK) { printf("%d;%f\n", nProc, finish - start); } MPI_Finalize(); return 0; }