/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2021 John Helly (j.c.helly@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* Standard headers */
#include
#include
/* Local headers */
#include "error.h"
/**
* @brief Given an array of structs of size element_size, send
* nr_send[i] elements to each node i. Allocates the receive
* buffer recvbuf to the appropriate size and returns its size
* in nr_recv_tot.
*
* @param nr_send Number of elements to send to each node
* @param nr_recv Number of elements to receive from each node
* @param sendbuf The elements to send
* @param recvbuf The output buffer
*
*/
void exchange_structs(size_t *nr_send, void *sendbuf, size_t *nr_recv,
void *recvbuf, size_t element_size) {
#ifdef WITH_MPI
/* Determine rank, number of ranks */
int nr_nodes, nodeID;
MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes);
MPI_Comm_rank(MPI_COMM_WORLD, &nodeID);
/* Compute send offsets */
size_t *send_offset = (size_t *)malloc(nr_nodes * sizeof(size_t));
send_offset[0] = 0;
for (int i = 1; i < nr_nodes; i += 1) {
send_offset[i] = send_offset[i - 1] + nr_send[i - 1];
}
/* Compute receive offsets */
size_t *recv_offset = (size_t *)malloc(nr_nodes * sizeof(size_t));
recv_offset[0] = 0;
for (int i = 1; i < nr_nodes; i += 1) {
recv_offset[i] = recv_offset[i - 1] + nr_recv[i - 1];
}
/* Allocate request objects (one send and receive per node) */
MPI_Request *request =
(MPI_Request *)malloc(2 * sizeof(MPI_Request) * nr_nodes);
/* Make type to communicate the struct */
MPI_Datatype value_mpi_type;
if (MPI_Type_contiguous(element_size, MPI_BYTE, &value_mpi_type) !=
MPI_SUCCESS ||
MPI_Type_commit(&value_mpi_type) != MPI_SUCCESS) {
error("Failed to create MPI type for struct to exchange.");
}
/*
* Post the send operations. This is an alltoallv really but
* we want to avoid the limits imposed by int counts and offsets
* in MPI_Alltoallv.
*/
for (int i = 0; i < nr_nodes; i += 1) {
if (nr_send[i] > 0) {
/* TODO: handle very large messages */
if (nr_send[i] > INT_MAX)
error("exchange_structs() fails if nr_send > INT_MAX!");
char *buf = (char *)sendbuf;
MPI_Isend(&(buf[send_offset[i] * element_size]), (int)nr_send[i],
value_mpi_type, i, 0, MPI_COMM_WORLD, &(request[i]));
} else {
request[i] = MPI_REQUEST_NULL;
}
}
/* Post the receives */
for (int i = 0; i < nr_nodes; i += 1) {
if (nr_recv[i] > 0) {
/* TODO: handle very large messages */
if (nr_recv[i] > INT_MAX)
error("exchange_structs() fails if nr_recv > INT_MAX!");
char *buf = (char *)recvbuf;
MPI_Irecv(&(buf[recv_offset[i] * element_size]), (int)nr_recv[i],
value_mpi_type, i, 0, MPI_COMM_WORLD, &(request[i + nr_nodes]));
} else {
request[i + nr_nodes] = MPI_REQUEST_NULL;
}
}
/* Wait for everything to complete */
MPI_Waitall(2 * nr_nodes, request, MPI_STATUSES_IGNORE);
/* Done with the MPI type */
MPI_Type_free(&value_mpi_type);
/* Tidy up */
free(recv_offset);
free(send_offset);
free(request);
#else
error("should only be called in MPI mode");
#endif
}