serkan maestro
serkan maestro

Reputation: 53

Sending array of structs in MPI

I have a data structure that looks like below

struct test{
  double a1;
  double a2;
}

and have a pointer test *info = nullptr; to beginning to the array of structs.

Question is how can I broadcast this array without converting it into two separate vector.

  1. I tried to create mpi_type_struct didn't work.
  2. I tried to pack my struct data didn't work. The cases I've seen so far are valid for only one struct but not array of structs.

This is what I have tried so far

  int block[2] = {1,1};
  MPI_Aint displacements[2];
  MPI_Aint baseaddr, addr1, addr2;
  MPI_Datatype types[2] = {MPI_DOUBLE, MPI_DOUBLE};
  MPI_Datatype contigs[6];
  if(my_rank==0){
    for (int i = 0; i < 6; i++)
    {
      MPI_Get_address ( &info[i], &baseaddr);
      MPI_Get_address ( &info[i].num_edges, &addr1);
      MPI_Get_address ( &info[i].num_vertices, &addr2);
      displs[0] = addr1 - baseaddr;
      displs[1] = addr2 - baseaddr;

      MPI_Type_create_struct(2, block, displacements, types, &contigs[i]);
      MPI_Type_commit(&contigs[i]);
    }
  MPI_Bcast(info, 6, *contigs, 0, comm);

Upvotes: 2

Views: 988

Answers (2)

Rob Latham
Rob Latham

Reputation: 5223

MPI_TYPE_STRUCT is not for describing C struct datatypes (though you can use it for that -- you can do anything with MPI_TYPE_STRUCT). It's for describing a datatype consisting of more than one datatype.

'double' is pretty safe, but in order to deal with member padding I think it's the right call to get the address of each item.

@dreamcrash's solution creates a single datatype and sends 'count' of that type. Another approach would be to describe the memory with MPI_TYPE_HINDEXED_BLOCK (since the blocksize is always sizeof(double).

Upvotes: 0

dreamcrash
dreamcrash

Reputation: 51443

  int block[2] = {1,1};
  MPI_Aint displacements[2];
  MPI_Aint baseaddr, addr1, addr2;
  MPI_Datatype types[2] = {MPI_DOUBLE, MPI_DOUBLE};
  MPI_Datatype contigs[6];
  if(my_rank==0){
    for (int i = 0; i < 6; i++)
    {
      MPI_Get_address ( &info[i], &baseaddr);
      MPI_Get_address ( &info[i].num_edges, &addr1);
      MPI_Get_address ( &info[i].num_vertices, &addr2);
      displs[0] = addr1 - baseaddr;
      displs[1] = addr2 - baseaddr;

      MPI_Type_create_struct(2, block, displacements, types, &contigs[i]);
      MPI_Type_commit(&contigs[i]);
    }

You don't have to build each struct in the array. You can instead MPI_Type_create_struct one of them and then just use the MPI_Bcast:

A full example:

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
void defineStruct(MPI_Datatype *tstype);

typedef struct S{
  double a1;
  double a2;
} S;

void defineStruct(MPI_Datatype *tstype) {
    const int count = 2;
    int          blocklens[count] = {1,1};
    MPI_Datatype types[2] = {MPI_DOUBLE, MPI_DOUBLE};
    MPI_Aint     disps[count] = {offsetof(S,a1), offsetof(S,a2)};

    MPI_Type_create_struct(count, blocklens, disps, types, tstype);
    MPI_Type_commit(tstype);
}

int main(int argc,char *argv[]){
    MPI_Init(NULL,NULL);
    int world_rank; 
    MPI_Comm_rank(MPI_COMM_WORLD,&world_rank);
    MPI_Datatype structtype;
    int total_size = 5;
    S *info = malloc(sizeof(S) * total_size);
    
    // Just adding some fake values
    if(world_rank == 0){   
        for(int i = 0; i < total_size; i++){
           info[i].a1 = i * i;
           info[i].a2 = i * (i+1);
        }
    }    

    defineStruct(&structtype);
    MPI_Bcast(info, total_size, structtype, 0, MPI_COMM_WORLD);  
          
    if(world_rank != 0){
      for(int i = 0; i < total_size; i++){
         printf("%lf %lf\n", info[i].a1, info[i].a2);
      }
    }
    free(info);
    MPI_Finalize();
    return 0;
 }

Output:

0.000000 0.000000
1.000000 2.000000
4.000000 6.000000
9.000000 12.000000
16.000000 20.000000

Upvotes: 2

Related Questions