Select Git revision
fmpi_mpi.c 12.49 KiB
// SPDX-License-Identifier: 0BSD
/*!
* @file
* @license{
* BSD Zero Clause License
*
* Copyright (c) 2022 by Raphael Bach
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* }
*/
/*==============================================================================
INCLUDE
==============================================================================*/
// Own header
#include "internal/fmpi_mpi.h"
// C Standard Library
#include <assert.h>
#include <limits.h> // INT_MAX
#include <stdbool.h>
#include <stdio.h> // printf()
#include <stdlib.h> // NULL, free(), malloc()
// External
#include <mpi.h>
// Internal
#include "internal/fmpi_common.h"
#include "internal/fmpi_error.h"
#include "internal/fmpi_type.h"
/*==============================================================================
DEFINE
==============================================================================*/
#define FMPI_MPI_ROOT 0
/*==============================================================================
MACRO
==============================================================================*/
#define FMPI_RAISE_MPI_ERROR(ctx, ...) \
do { \
if((ctx)->err_handler.func != NULL) { \
FMPI_RAISE_ERROR((ctx)->err_handler, "MPI", __VA_ARGS__); \
} \
} while(0)
/*==============================================================================
PUBLIC FUNCTION DEFINITION
==============================================================================*/
/*------------------------------------------------------------------------------
fmpi_mpi_init()
------------------------------------------------------------------------------*/
struct fmpi_mpi_ctx * fmpi_mpi_init(
int * const argc, char ** argv[],
const struct fmpi_error_handler err_handler
){
struct fmpi_mpi_ctx * ctx = malloc(sizeof(*ctx));
if(ctx == NULL) {
FMPI_RAISE_ERROR(err_handler, "FMPI", "malloc(fmpi_mpi_ctx) failed!");
return NULL;
}
ctx->err_handler = err_handler;
ctx->root = FMPI_MPI_ROOT;
int err = MPI_Init(argc, argv);
if(fmpi_mpi_check_error(ctx, err, "MPI_Init") != FMPI_SUCCESS) {
free(ctx);
return NULL;
}
err = MPI_Comm_dup(MPI_COMM_WORLD, &ctx->world);
if(fmpi_mpi_check_error(ctx, err, "MPI_Comm_dup") != FMPI_SUCCESS) {
fmpi_mpi_exit(&ctx);
return NULL;
}
err = MPI_Comm_rank(ctx->world, &ctx->rank);
if(fmpi_mpi_check_error(ctx, err, "MPI_Comm_rank") != FMPI_SUCCESS) {
fmpi_mpi_exit(&ctx);
return NULL;
}
err = MPI_Comm_size(ctx->world, &ctx->size);
if(fmpi_mpi_check_error(ctx, err, "MPI_Comm_size") != FMPI_SUCCESS) {
fmpi_mpi_exit(&ctx);
return NULL;
}
// `ctx->size` is used with `malloc()` and `malloc(0)` is implementation
// defined.
assert(ctx->size > 0);
err = MPI_Get_processor_name(ctx->cpu_name, &ctx->cpu_name_length);
fmpi_mpi_check_error(ctx, err, "MPI_Get_processor_name");
return ctx;
}
/*------------------------------------------------------------------------------
fmpi_mpi_exit()
------------------------------------------------------------------------------*/
int fmpi_mpi_exit(struct fmpi_mpi_ctx ** const ctx)
{
assert(ctx != NULL);
assert(*ctx != NULL);
int err = FMPI_SUCCESS;
if(fmpi_mpi_finalized(*ctx) == false) {
err = MPI_Finalize();
err = fmpi_mpi_check_error(*ctx, err, "MPI_Finalize");
}
free(*ctx); *ctx = NULL;
return err;
}
/*------------------------------------------------------------------------------
fmpi_mpi_is_root()
------------------------------------------------------------------------------*/
_Bool fmpi_mpi_is_root(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
return ctx->rank == ctx->root;
}
/*------------------------------------------------------------------------------
fmpi_mpi_check_error()
------------------------------------------------------------------------------*/
int fmpi_mpi_check_error(
const struct fmpi_mpi_ctx * ctx, int err, const char * const func
){
assert(ctx != NULL);
assert(func != NULL);
if(err != MPI_SUCCESS) {
char err_str[MPI_MAX_ERROR_STRING] = {'\0'};
int err_str_len = 0;
err = MPI_Error_string(err, err_str, &err_str_len);
if(err != MPI_SUCCESS) {
FMPI_RAISE_MPI_ERROR(ctx, "MPI_Error_string() failed!");
}
FMPI_RAISE_MPI_ERROR(ctx, "%s() failed! %s", func, err_str);
return FMPI_ERR_MPI;
}
return FMPI_SUCCESS;
}
/*------------------------------------------------------------------------------
fmpi_mpi_ctx_print()
------------------------------------------------------------------------------*/
void fmpi_mpi_ctx_print(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
printf("MPI size: %d\n", ctx->size);
printf("MPI rank: %d\n", ctx->rank);
printf("MPI CPU name: %s\n", ctx->cpu_name);
printf("MPI root: %s\n", ctx->cpu_name);
printf("MPI futhark error class: %d\n", ctx->futhark_err_class);
printf("MPI futhark error code: %d\n", ctx->futhark_err_code);
}
/*------------------------------------------------------------------------------
fmpi_mpi_world_rank()
------------------------------------------------------------------------------*/
int fmpi_mpi_world_rank(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
int rank = FMPI_ERROR;
int err = MPI_Comm_rank(ctx->world, &rank);
err = fmpi_mpi_check_error(ctx, err, "MPI_Comm_rank");
if(err != FMPI_SUCCESS) {
return err;
}
return rank;
}
/*------------------------------------------------------------------------------
fmpi_mpi_finalized()
------------------------------------------------------------------------------*/
_Bool fmpi_mpi_finalized(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
int finalized = 0;
const int err = MPI_Finalized(&finalized);
if(err != MPI_SUCCESS) {
FMPI_RAISE_MPI_ERROR(ctx, "MPI_Finalized() failed with err=%d!", err);
}
return finalized;
}
/*------------------------------------------------------------------------------
fmpi_mpi_abort()
------------------------------------------------------------------------------*/
int fmpi_mpi_abort(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
if(fmpi_mpi_finalized(ctx) == false) {
const int err = MPI_Abort(ctx->world, MPI_ERR_UNKNOWN);
return fmpi_mpi_check_error(ctx, err, "MPI_Abort");
}
return FMPI_ERROR;
}
/*------------------------------------------------------------------------------
fmpi_mpi_world_barrier()
------------------------------------------------------------------------------*/
int fmpi_mpi_world_barrier(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
const int err = MPI_Barrier(ctx->world);
return fmpi_mpi_check_error(ctx, err, "MPI_Barrier");
}
/*------------------------------------------------------------------------------
fmpi_mpi_type()
------------------------------------------------------------------------------*/
MPI_Datatype fmpi_mpi_type(const enum fmpi_type_base type)
{
static const MPI_Datatype fmpi_mpi_type_list[] = {
[FMPI_TYPE_i8] = MPI_INT8_T ,
[FMPI_TYPE_i16] = MPI_INT16_T ,
[FMPI_TYPE_i32] = MPI_INT32_T ,
[FMPI_TYPE_i64] = MPI_INT64_T ,
[FMPI_TYPE_u8] = MPI_UINT8_T ,
[FMPI_TYPE_u16] = MPI_UINT16_T,
[FMPI_TYPE_u32] = MPI_UINT32_T,
[FMPI_TYPE_u64] = MPI_UINT64_T,
[FMPI_TYPE_f32] = MPI_FLOAT ,
[FMPI_TYPE_f64] = MPI_DOUBLE ,
[FMPI_TYPE_bool] = MPI_C_BOOL
};
return fmpi_mpi_type_list[type];
}
/*------------------------------------------------------------------------------
fmpi_mpi_gather_in_place()
------------------------------------------------------------------------------*/
int fmpi_mpi_gather_in_place(
const struct fmpi_mpi_ctx * const ctx, void * const buf, MPI_Datatype type,
const size_t send_cnt, const size_t recv_cnt, const int root, MPI_Comm comm
) {
assert(ctx != NULL);
assert(buf != NULL);
assert(send_cnt <= INT_MAX);
assert(recv_cnt <= INT_MAX);
assert(send_cnt <= recv_cnt);
int err = MPI_SUCCESS;
if(ctx->rank == root) {
err = MPI_Gather(
MPI_IN_PLACE, (int)send_cnt, type,
buf , (int)recv_cnt, type,
root, comm
);
} else {
err = MPI_Gather(
buf, (int)send_cnt, type,
buf, (int)recv_cnt, type,
root, comm
);
}
return fmpi_mpi_check_error(ctx, err, "MPI_Gather");
}
/*------------------------------------------------------------------------------
fmpi_mpi_world_gather_in_place()
------------------------------------------------------------------------------*/
int fmpi_mpi_world_gather_in_place(
const struct fmpi_mpi_ctx * const ctx, void * const buf, MPI_Datatype type,
const size_t send_cnt, const size_t recv_cnt
){
assert(ctx != NULL);
assert(buf != NULL);
assert(send_cnt <= INT_MAX);
assert(recv_cnt <= INT_MAX);
assert(send_cnt <= recv_cnt);
const int err = fmpi_mpi_gather_in_place(
ctx, buf, type, send_cnt, recv_cnt, ctx->root, ctx->world
);
if(err != FMPI_SUCCESS) {
FMPI_RAISE_ERROR(ctx->err_handler, "FMPI",
"fmpi_mpi_gather_in_place() failed!"
);
}
return err;
}
/*------------------------------------------------------------------------------
fmpi_mpi_reduce_in_place()
------------------------------------------------------------------------------*/
int fmpi_mpi_reduce_in_place(
const struct fmpi_mpi_ctx * const ctx, void * const buf, const size_t cnt,
MPI_Datatype type, MPI_Op op, const int root, MPI_Comm comm
){
assert(ctx != NULL);
assert(buf != NULL);
assert(cnt <= INT_MAX);
int err = MPI_SUCCESS;
if(ctx->rank == root) {
err = MPI_Reduce(MPI_IN_PLACE, buf, (int)cnt, type, op, root, comm);
} else {
err = MPI_Reduce(buf, buf, (int)cnt, type, op, root, comm);
}
return fmpi_mpi_check_error(ctx, err, "MPI_Reduce");
}
/*------------------------------------------------------------------------------
fmpi_mpi_world_reduce_in_place()
------------------------------------------------------------------------------*/
int fmpi_mpi_world_reduce_in_place(
const struct fmpi_mpi_ctx * const ctx, void * const buf, const size_t cnt,
MPI_Datatype type, MPI_Op op
){
assert(ctx != NULL);
assert(buf != NULL);
assert(cnt <= INT_MAX);
const int err = fmpi_mpi_reduce_in_place(
ctx, buf, cnt, type, op, ctx->root, ctx->world
);
if(err != FMPI_SUCCESS) {
FMPI_RAISE_ERROR(ctx->err_handler, "FMPI",
"fmpi_mpi_reduce_in_place() failed!"
);
}
return err;
}
/*------------------------------------------------------------------------------
fmpi_mpi_dims_create()
------------------------------------------------------------------------------*/
int fmpi_mpi_dims_create(
const struct fmpi_mpi_ctx * const ctx, size_t * const dim_len,
const size_t dim_cnt
){
assert(ctx != NULL);
assert(dim_len != NULL);
assert(dim_cnt <= FMPI_DIM_MAX);
int dims[FMPI_DIM_MAX];
for(size_t i = 0; i < dim_cnt; i++) {
dims[i] = (int)dim_len[i];
}
const int err = MPI_Dims_create(ctx->size, (int)dim_cnt, dims);
for(size_t i = 0; i < dim_cnt; i++) {
assert(dims[i] >= 0);
dim_len[i] = (size_t)dims[i];
}
return fmpi_mpi_check_error(ctx, err, "MPI_Dims_create");
}
/*------------------------------------------------------------------------------
fmpi_mpi_world_size()
------------------------------------------------------------------------------*/
int fmpi_mpi_world_size(const struct fmpi_mpi_ctx * const ctx)
{
assert(ctx != NULL);
int size = FMPI_ERROR;
int err = MPI_Comm_size(ctx->world, &size);
err = fmpi_mpi_check_error(ctx, err, "MPI_Comm_size");
if(err != FMPI_SUCCESS) {
return err;
}
return size;
}