Scotty
Scotty

Reputation: 43

MPI send dynamic array of BYTE

I want to send a struct that contains some variables and an array dynamically allocated. I read that it's not possible to send all at one because of the dynamic array. I should send first a message with all the other variables and then another message with the dynamic array.

Because of that, I thought I could send directly a copy of the content of the struct in an dynamic array of BYTE (look at the code for more details).

I wrote the following code.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mpi.h>

// I read MPI_BYTE is an unsigned char
typedef unsigned char BYTE;

typedef struct Message
{   
    int id;
    int detectNr;
    char *detection;
} Msg;

int main() {
    int size, rank;

    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (size < 2) {
        printf("Invalid number of processes.\n");
        return -1;
    }

    // initialize an empty message
    Msg msg = {.id = 1, .detectNr = 0, .detection = NULL};

    // here we should take the measurements
    // for now suppose there are 10
    msg.detectNr = 10;
    msg.detection = malloc(sizeof(char) * msg.detectNr);

    for (int i = 0; i < msg.detectNr; i++)
        msg.detection[i] = 'a' + i;

    if(rank != 0) {
         // put the data inside a buffer of BYTE
        int bufferSize = sizeof(int) + sizeof(int) + sizeof(char) * msg.detectNr;
        BYTE *buffer = malloc(sizeof(BYTE) * bufferSize);
        memcpy(buffer, &msg.id, sizeof(int));
        memcpy(buffer + sizeof(int), &msg.detectNr, sizeof(int));
        memcpy(buffer + (2 * sizeof(int)), &msg.detection, sizeof(char) * msg.detectNr);

        // send buffer to process 0
        MPI_Send(buffer, bufferSize, MPI_BYTE, 0, 0, MPI_COMM_WORLD);

        free(buffer);
        free(msg.detection);
    } else {
        for (int i = 1; i < size; i++) {
            int bufferSize;
            BYTE *buffer;
            MPI_Status status;

            // initialize an empty message
            Msg rcv= {.id = 0, .detectNr = 0, .detection = NULL};

            // probe for an incoming message from process zero
            MPI_Probe(i, 0, MPI_COMM_WORLD, &status);

            // when probe returns, the status object has the size and other
            // attributes of the incoming message
            // get the message size
            MPI_Get_count(&status, MPI_BYTE, &bufferSize);

            printf("\nProcess %d: buffer size: %d\n", rank, bufferSize);

            // allocate a buffer to hold the incoming data
            buffer = malloc(sizeof(BYTE) * bufferSize);
            
            // now receive the message with the allocated buffer
            MPI_Recv(buffer, bufferSize, MPI_BYTE, i, 0, MPI_COMM_WORLD, &status);

            // copy the data from the buffer to the message
            memcpy(&rcv.id, buffer, sizeof(int));
            memcpy(&rcv.detectNr, buffer + sizeof(int), sizeof(int));
            memcpy(&rcv.detection, buffer + (2 * sizeof(int)), sizeof(char) * rcv.detectNr);

            printf("Process %d: id: %d\n", rank, rcv.id);
            printf("Process %d: detectNr: %d\n", rank, rcv.detectNr);
            printf("Process %d: detection: %s\n", rank, rcv.detection);
            
            free(rcv.detection);
            free(buffer);
        }
    }

    MPI_Finalize();

    return 0;
}

Unfortunately it doesn't work, here below the result.

Process 0: buffer size: 18
Process 1: id: 1
Process 1: detectNr: 10

YOUR APPLICATION TERMINATED WITH THE EXIT STRING: Segmentation fault (signal 11)

It looks like, and probably it is so, if the last part of the buffer used for receiving is empty. I don't understand why it happens, I'm not trying to send the original array detection, but instead I'm allocating an entire new dynamic array and I'm copying all the values inside.

Can you help me to solve the problem, or can you explain to me why it doesn't works?

I don't know if this can help, I'm developing in C, under Ubuntu, with VSCode as editor and gcc as compiler.

Upvotes: 1

Views: 156

Answers (1)

Scotty
Scotty

Reputation: 43

Ok, basically the problem wasn't MPI, but the memory allocation, so my fault.

Here below the right code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mpi.h>

// I read MPI_BYTE is an unsigned char
typedef unsigned char BYTE;

typedef struct Message
{   
    int id;
    int detectNr;
    char *detection;
} Msg;

int main() {
    int size, rank;

    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (size < 2) {
        printf("Invalid number of processes.\n");
        return -1;
    }

    if(rank != 0) {

        // initialize an empty message
        Msg msg = {.id = 1, .detectNr = 0, .detection = NULL};

        // here we should take the measurements
        // for now suppose there are 10
        msg.detectNr = 10;
        msg.detection = malloc(sizeof(char) * msg.detectNr);

        for (i = 0; i < msg.detectNr; i++)
            msg.detection[i] = 'a' + i;

        printf("Process %d: id: %d\n", rank, msg.id);
        printf("Process %d: detectNr: %d\n", rank, msg.detectNr);

        for (i = 0; i < msg.detectNr; i++)
            printf("Process %d: msg.detect[%d]: %c\n", rank, i, msg.detection[i]);

        for (int i = 0; i < msg.detectNr; i++)
            msg.detection[i] = 'a' + i;

        printf("Process %d: i: %d\n", rank, i);

        // put the data inside a buffer of BYTE
        int bufferSize = sizeof(int) + sizeof(int) + sizeof(char) * msg.detectNr;
        BYTE *buffer = malloc(sizeof(BYTE) * bufferSize);
        memcpy(buffer, &msg.id, sizeof(int));
        memcpy(buffer + sizeof(int), &msg.detectNr, sizeof(int));
        memcpy(buffer + (2 * sizeof(int)), msg.detection, sizeof(char) * msg.detectNr);

        printf("\nProcess %d: buffer size: %d\n", rank, bufferSize);

        // send buffer to process 0
        MPI_Send(buffer, bufferSize, MPI_BYTE, 0, 0, MPI_COMM_WORLD);

        free(buffer);
        free(msg.detection);
    } else {

        for (int i = 1; i < size; i++) {
            int bufferSize;
            BYTE *buffer;
            MPI_Status status;

            // initialize an empty message
            Msg rcv = {.id = 0, .detectNr = 0, .detection = NULL};

            // probe for an incoming message from process zero
            MPI_Probe(i, 0, MPI_COMM_WORLD, &status);

            // when probe returns, the status object has the size and other
            // attributes of the incoming message
            // get the message size
            MPI_Get_count(&status, MPI_BYTE, &bufferSize);

            printf("\nProcess %d: buffer size: %d\n", rank, bufferSize);

            // allocate a buffer to hold the incoming data
            buffer = malloc(sizeof(BYTE) * bufferSize);
            
            // now receive the message with the allocated buffer
            MPI_Recv(buffer, bufferSize, MPI_BYTE, i, 0, MPI_COMM_WORLD, &status);

            // copy the data from the buffer to the message
            memcpy(&rcv.id, buffer, sizeof(int));
            memcpy(&rcv.detectNr, buffer + sizeof(int), sizeof(int));

            rcv.detection = malloc(sizeof(char) * rcv.detectNr);
            memcpy(rcv.detection, buffer + (2 * sizeof(int)), sizeof(char) * rcv.detectNr);

            printf("Process %d: id: %d\n", rank, rcv.id);
            printf("Process %d: detectNr: %d\n", rank, rcv.detectNr);

            for (i = 0; i < rcv.detectNr; i++)
                printf("Process %d: rcv.detect[%d]: %c\n", rank, i, rcv.detection[i]);

            printf("Process %d: i: %d\n", rank, i);
            
            free(rcv.detection);
            free(buffer);
        }
    }

    MPI_Finalize();

    return 0;
}

Upvotes: 1

Related Questions