Proper way to receive a data structure with MPI_Gather - c

I have the following data structure which I'm trying to send with MPI_Gather:
struct set {
int nbits;
char bits[];
};
Problem is that I'm unable to gather all items of the above structure, only the first item. The remaining items simply doesn't make sense.
Here is a testcase:
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
#define SIZE 10
struct set {
int nbits;
char bits[];
};
int main(int argc, char *argv[]) {
int np, rank, i;
struct set *subsets, *single;
void *buf;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &np);
single = malloc(sizeof(struct set) + SIZE);
if(rank == 0) {
subsets = malloc((sizeof(struct set) + SIZE) * np);
}
buf = &subsets[0];
MPI_Datatype set_type, oldtypes[2];
int blockcounts[2];
MPI_Aint offsets[2];
MPI_Aint addr[3];
MPI_Get_address(single, &addr[0]);
MPI_Get_address(&single->nbits, &addr[1]);
MPI_Get_address(&single->bits, &addr[2]);
offsets[0] = addr[1] - addr[0];
oldtypes[0] = MPI_INT;
blockcounts[0] = 1;
offsets[1] = addr[2] - addr[0];
oldtypes[1] = MPI_CHAR;
blockcounts[1] = SIZE;
MPI_Type_create_struct(2, blockcounts, offsets, oldtypes, &set_type);
MPI_Type_commit(&set_type);
single->nbits = 2;
for(i=0; i<single->nbits; i++)
single->bits[i] = 'A' + rank;
MPI_Gather(single, 1, set_type, buf, 1, set_type, 0, MPI_COMM_WORLD);
if(rank == 0) {
void *ptr;
struct set *fs;
int size;
MPI_Type_size(set_type, &size);
ptr = buf;
for(i=0; i<np; i++) {
size_t j;
fs = ptr;
printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);
for(j=0; j<2; j++)
printf("from rank %d: buf[%d] = %#x\n",
i, j, fs->bits[j]);
ptr += size;
}
}
MPI_Type_free(&set_type);
MPI_Finalize();
}
Any help would be appreciated.

The problem isn't so much in the MPI as in the pointer arithmetic with structs and with MPI types.
You have
void *ptr;
struct set *fs;
int size;
MPI_Type_size(set_type, &size);
ptr = buf;
for(i=0; i<np; i++) {
size_t j;
fs = ptr;
printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);
for(j=0; j<2; j++)
printf("from rank %d: buf[%d] = %#x\n",
i, j, fs->bits[j]);
ptr += size;
}
}
But MPI_Type_size actually gives the amount of data in the type; if there's padding (which there probably will be here to get the character array to be on a word boundary) this isn't the same as sizeof. If you want to use MPI functions here, if you switch that one function call to MPI_Type_extent, which actually tells you the entire extent spanned by the type, your code runs for me... but there's still a problem.
If you take a look at the difference between sizeof(struct set)+SIZE and MPI_Type_extent() you'll see they're not the same; this:
#define SIZE 10
struct set {
int nbits
char nbits[]
}
...
malloc(sizeof(struct set)+SIZE);
isn't the same as
struct set {
int nbits
char nbits[SIZE]
}
malloc(sizeof(struct set));
because of padding, etc. This means that the size of subsets is wrong, and there's a memory error when you call MPI_Gather.
You can get around this in a few different ways, but the simplest (and shortest in terms of line count) is to define the structure with the array already sized, and then use array indexing instead of pointer arithmetic:
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
#define SIZE 10
struct set {
int nbits;
char bits[SIZE];
};
int main(int argc, char *argv[]) {
int np, rank, i;
struct set *subsets, *single;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &np);
single = malloc(sizeof(struct set));
if(rank == 0) {
subsets = malloc(sizeof(struct set) * np);
}
MPI_Datatype set_type, oldtypes[2];
int blockcounts[2];
MPI_Aint offsets[2];
MPI_Aint addr[3];
MPI_Get_address(single, &addr[0]);
MPI_Get_address(&single->nbits, &addr[1]);
MPI_Get_address(&single->bits, &addr[2]);
offsets[0] = addr[1] - addr[0];
oldtypes[0] = MPI_INT;
blockcounts[0] = 1;
offsets[1] = addr[2] - addr[0];
oldtypes[1] = MPI_CHAR;
blockcounts[1] = SIZE;
MPI_Type_create_struct(2, blockcounts, offsets, oldtypes, &set_type);
MPI_Type_commit(&set_type);
single->nbits = 2;
for(i=0; i<single->nbits; i++)
single->bits[i] = 'A' + rank;
MPI_Gather(single, 1, set_type, &(subsets[0]), 1, set_type, 0, MPI_COMM_WORLD);
if(rank == 0) {
for(i=0; i<np; i++) {
struct set *fs = &(subsets[i]);
printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);
for(int j=0; j<2; j++)
printf("from rank %d: buf[%d] = %#x\n",
i, j, fs->bits[j]);
}
}
MPI_Type_free(&set_type);
MPI_Finalize();
}
Updated to add And if you can't do that, just change the size of your buffer alloc to gather the data into:
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
#define SIZE 10
struct set {
int nbits;
char bits[];
};
int main(int argc, char *argv[]) {
int np, rank, i;
struct set *single;
void *buf;
ptrdiff_t extent;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &np);
single = malloc(sizeof(struct set) + SIZE);
MPI_Datatype set_type, oldtypes[2];
int blockcounts[2];
MPI_Aint offsets[2];
MPI_Aint addr[3];
MPI_Get_address(single, &addr[0]);
MPI_Get_address(&single->nbits, &addr[1]);
MPI_Get_address(&single->bits, &addr[2]);
offsets[0] = addr[1] - addr[0];
oldtypes[0] = MPI_INT;
blockcounts[0] = 1;
offsets[1] = addr[2] - addr[0];
oldtypes[1] = MPI_CHAR;
blockcounts[1] = SIZE;
MPI_Type_create_struct(2, blockcounts, offsets, oldtypes, &set_type);
MPI_Type_commit(&set_type);
MPI_Type_extent(set_type, &extent);
buf = malloc((int)extent * np);
single->nbits = 2;
for(i=0; i<single->nbits; i++)
single->bits[i] = 'A' + rank;
MPI_Gather(single, 1, set_type, buf, 1, set_type, 0, MPI_COMM_WORLD);
if(rank == 0) {
struct set *fs = buf;
for(i=0; i<np; i++) {
printf("from rank %d: bits => %p nbits => %d\n", i, fs->bits, fs->nbits);
for(int j=0; j<2; j++)
printf("from rank %d: buf[%d] = %#x\n",
i, j, fs->bits[j]);
fs = (struct set *)((char *)fs + extent);
}
}
MPI_Type_free(&set_type);
MPI_Finalize();
}

Related

Send over MPI_Bcast an MPI_PACKED message

I have a message that I want to send in broadcast by using MPI_Bcast.
I have two structs with, near the others, dynamic arrays, and because of that I decided to use MPI_Pack and MPI_Unpack.
Here below my solution.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
#include <stddef.h>
#define DEBUG 0
typedef struct Code {
char character;
int length;
char *code;
} Code;
typedef struct CodeDictionary {
int codesNr;
Code *codes;
} CodeDictionary;
typedef struct Header {
int size; // size of the message in bytes
MPI_Datatype *type; // message type
int position; // position in the buffer
} Header;
typedef unsigned char BYTE;
int getRand(const int from, const int to)
{
int num = (rand() % (to - from + 1)) + from;
return num;
}
void buildCodeDictionaryType(MPI_Datatype *CodeDictType) {
int blockLengths[] = {1, 1};
MPI_Datatype types[] = {MPI_CHAR, MPI_INT};
MPI_Aint offsets[2];
offsets[0] = offsetof(Code, character);
offsets[1] = offsetof(Code, length);
MPI_Type_create_struct(2, blockLengths, offsets, types, CodeDictType);
MPI_Type_commit(CodeDictType);
}
BYTE* buildCodeDictionaryMsg(Header *header, CodeDictionary *dict) {
header->size = sizeof(int);
BYTE *buffer = malloc(sizeof(BYTE) * (header->size));
MPI_Pack(&dict->codesNr, 1, MPI_INT, buffer, header->size, &header->position, MPI_COMM_WORLD);
if (DEBUG == 1) {
printf("\ndict->codesNr = %d\n", dict->codesNr);
printf("header->size = %d\n", header->size);
printf("header->position = %d\n\n", header->position);
}
for (int i = 0; i < dict->codesNr; i++) {
header->size += sizeof(char) + sizeof(int) + (sizeof(char) * (dict->codes[i].length+1));
buffer = realloc(buffer, header->size);
MPI_Pack(&dict->codes[i], 1, *header->type, buffer, header->size, &header->position, MPI_COMM_WORLD);
if (DEBUG == 1) {
printf("before pack array - header->size = %d\n", header->size);
printf("before pack array - header->position = %d\n", header->position);
}
MPI_Pack(dict->codes[i].code, (dict->codes[i].length+1), MPI_CHAR, buffer, header->size, &header->position, MPI_COMM_WORLD);
if (DEBUG == 1) {
printf("after pack array - header->size = %d\n", header->size);
printf("after pack array - header->position = %d\n", header->position);
printf("\n");
}
}
return buffer;
}
void buildCodeDictionary(Header *header, CodeDictionary *dict, BYTE* buffer) {
MPI_Unpack(buffer, header->size, &header->position, &dict->codesNr, 1, MPI_INT, MPI_COMM_WORLD);
if (DEBUG == 1) {
printf("dict->codesNr = %d\n", dict->codesNr);
printf("header->size = %d\n", header->size);
printf("header->position = %d\n\n", header->position);
}
dict->codes = malloc(sizeof(Code) * dict->codesNr);
// I do it just for the first element because of test
MPI_Unpack(buffer, header->size, &header->position, &dict->codes[0], 1, *header->type, MPI_COMM_WORLD);
if (DEBUG == 1) {
printf("before unpack - header->size = %d\n", header->size);
printf("before unpack - header->position = %d\n", header->position);
}
dict->codes[0].code = malloc(sizeof(char) * (dict->codes[0].length+1));
MPI_Unpack(buffer, header->size, &header->position, &dict->codes[0].code, (dict->codes[0].length+1), MPI_CHAR, MPI_COMM_WORLD);
if (DEBUG == 1) {
printf("after unpack - header->size = %d\n", header->size);
printf("after unpack - header->position = %d\n", header->position);
}
// just for test
printf("character: %c\tlength: %d\tcode: ",
dict->codes[0].character,
dict->codes[0].length);
printf("%s\n", dict->codes[0].code); // it crashes here
// if it works do a for in order to unpack all the data
// ...
}
int main(int argc, char** argv) {
MPI_Init(NULL, NULL);
// Get the number of processes
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// Get the rank of the process
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
// Create a CodeDictionary variable
CodeDictionary dict;
if (world_rank == 0)
printf("sending\n\n");
if (world_rank == 0) {
dict.codesNr = getRand(5, 9);
dict.codes = malloc(sizeof(Code) * dict.codesNr);
// create some fake values
for (int i = 0; i < dict.codesNr; i++) {
dict.codes[i].character = 'a' + i;
dict.codes[i].length = getRand(1, 9);
dict.codes[i].code = malloc(sizeof(char) * (dict.codes[i].length+1));
for (int j = 0; j < dict.codes[i].length; j++) {
int randChar = getRand('a', 'z');
dict.codes[i].code[j] = randChar + j;
}
dict.codes[i].code[dict.codes[i].length] = '\0';
if (DEBUG == 1)
printf("strlen(dict.charEncoding[%d].encoding): %d\n", i, strlen(dict.codes[i].code));
}
printf("source data\n");
for (int i = 0; i < dict.codesNr; i++) {
printf("codes[%d]:\n\t", i);
printf("character: %c\tlength: %d\tcode: ", dict.codes[i].character, dict.codes[i].length);
for (int j = 0; j < dict.codes[i].length; j++)
printf("%c", dict.codes[i].code[j]);
printf("\n");
}
}
MPI_Datatype codeDictType;
buildCodeDictionaryType(&codeDictType);
Header header = {.size = 0, .position = 0, .type = NULL};
header.type = &codeDictType;
BYTE *buffer = NULL;
if (world_rank == 0)
buffer = buildCodeDictionaryMsg(&header, &dict);
MPI_Bcast(&header.size, 1, MPI_INT, 0, MPI_COMM_WORLD);
if (DEBUG == 1)
printf("rank %d: header.size = %d\n", world_rank, header.size);
if (world_rank != 0)
buffer = calloc(header.size, sizeof(BYTE));
MPI_Bcast(buffer, header.size, MPI_PACKED, 0, MPI_COMM_WORLD);
if (world_rank != 0) {
printf("\nreceiving\n\n");
buildCodeDictionary(&header, &dict, buffer);
// printf("received data\n");
// for (int i = 0; i < dict.codesNr; i++) {
// printf("codes[%d]:\n\t", i);
// printf("character: %c\tlength: %d\tcode: ", dict.codes[i].character, dict.codes[i].length);
// for (int j = 0; j < dict.codes[i].length; j++)
// printf("%c", dict.codes[i].code[j]);
// printf("\n");
// }
}
free(buffer);
MPI_Type_free(&codeDictType);
for (int i = 0; i < dict.codesNr; i++)
free(dict.codes[i].code);
free(dict.codes);
MPI_Finalize();
return 0;
}
For some reason this line of code doesn't work MPI_Unpack(buffer, header->size, &header->position, &dict->codes[0].code, (dict->codes[0].length+1), MPI_CHAR, MPI_COMM_WORLD); because if I try to print the received array, the run crashes with segmentation fault as error.
I don't understand why it happens, I take care of the memory by allocating the right size, \0 character included.
Do you know what is the problem?

How to share a string array using open mpi

I'm new in openmpi and I don't know how to use scatter and gather to send an array of strings to all processors. I would like to divide an array and send it to each processor, but all I can divide are the characters of a single array element. Can anyone help me please?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpi.h"
#define MASTER 0
#define BUF_SIZE 2048
#define CHAR_SIZE 900
#define CHARS 13
#define MAX_SIZE 3500
#define NUMBER_OF_FILES 2
int main(int argc, char** argv) {
int number_of_words = 0;
int total_rows = 0;
int i, j = 0;
char **words = (char**) calloc(MAX_SIZE, sizeof (char*));
for (i = 0; i < MAX_SIZE; i++) {
words[i] = (char*) calloc(CHARS, sizeof (char));
}
char **local_words = (char**) calloc(MAX_SIZE, sizeof (char*));
for (i = 0; i < MAX_SIZE; i++) {
local_words[i] = (char*) calloc(CHARS, sizeof (char));
}
char **rec_words = (char**) calloc(MAX_SIZE, sizeof (char*));
for (i = 0; i < MAX_SIZE; i++) {
rec_words[i] = (char*) calloc(CHARS, sizeof (char));
}
char str_righe[BUF_SIZE][CHAR_SIZE];
FILE *f = NULL;
char f_title[10];
char str_nfiles[10];
char delim[10] = {10, 32, 33, 39, 44, 46, 58, 59, 63};
char *ptr;
int rank;
int size;
int message_length;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
for (i = 1; i <= NUMBER_OF_FILES; i++) {
strcpy(f_title, "f");
sprintf(str_nfiles, "%d", i);
strcat(f_title, str_nfiles);
strcat(f_title, ".txt");
f = fopen(f_title, "r");
while (fgets(str_righe[j], BUF_SIZE, f)) {
str_righe[j][strlen(str_righe[j])] = '\0';
j++;
}
fclose(f);
}
total_rows = j;
for (i = 0; i < total_rows; ++i) {
ptr = strtok(str_righe[i], delim);
while (ptr != NULL) {
strcpy(words[number_of_words], ptr);
ptr = strtok(NULL, delim);
number_of_words++;
}
}
message_length = number_of_words / size;
if (rank == MASTER) {
for (i = 0; i < number_of_words; i++)
printf("%s\n", words[i]);
}
MPI_Scatter(*words, message_length, MPI_CHAR, *local_words, message_length, MPI_CHAR, MASTER, MPI_COMM_WORLD);
printf("rank %d, fragment: \t%s\n", rank, *local_words);
MPI_Gather(*local_words, message_length, MPI_CHAR, *rec_words, message_length, MPI_CHAR, MASTER, MPI_COMM_WORLD);
if (rank == MASTER) {
printf("rank %d, gathered: \t%s\n", rank, *rec_words);
}
MPI_Finalize();
return EXIT_SUCCESS;
}
I expect the output:
iMac-di-iMac01:mpi macbook$ mpirun -n 2 main
Good
time
by
antonio
rank 0, fragment: Good time
rank 1, fragment: by antonio
rank 0, gathered: Good time by antonio
But the actual output is:
iMac-di-iMac01:mpi macbook$ mpirun -n 2 main
Good
time
by
antonio
rank 0, fragment: Go
rank 1, fragment: od
rank 0, gathered: Good
I realized that I never shared the solution to the problem. I do it now:
I created the matrix variable and I sent the one with the scatter. In this way the slaves received the words and not the characters
int *matrix = 0;
matrix = malloc(sizeof (int) * n_words);
j = 1;
for (i = 0; i <= n_words; i++) {
matrix[i] = j;
j++;
}
n_words_cpu = n_words / (size);
procRow = malloc(sizeof (int) * n_words); // received row will contain p integers
MPI_Scatter(
/* send_data = */ matrix,
/* send_count = */ n_words_cpu,
/* send_datatype = */ MPI_INT,
/* recv_data = */ procRow,
/* recv_count = */ n_words_cpu,
/* recv_datatype = */ MPI_INT,
/* root = */ MASTER,
/* MPI_commuicator = */ MPI_COMM_WORLD);

c mpi (spawn scatter gather) for variable number of processes

I am building up an example with variable no. of processes and bind them to the sockets in a small network with different architecture and number of cpus.
I compile and run with:
mpiicpc avg_4.c -qopenmp -axSSE4.2,AVX,CORE-AVX2 -O3 -par-affinity=noverbose,granularity=core,compact -o b
mpiexec.hydra -machinefile f19 -genv I_MPI_PIN=1 -genv I_MPI_PIN_DOMAIN=socket -genv I_MPI_PIN_ORDER=compact -n 1 ./b
The network (master + slave19) f19 is:
s19:1
ma:1
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <sched.h>
#include <mpi.h>
int *create_mlu(int n_omp, int ws) {
int *mlu = (int *)calloc(n_omp * ws, sizeof(int));
for (int i = 0; i < ws; i++)
for (int j = 0; j < n_omp; j++)
mlu[j + i*n_omp] = j + 100 * i;
return mlu;
}
int *C4_Re(int *mal, int n_omp, int wr, int ws) {
int *rM8 = (int *)malloc(sizeof(int) * n_omp);
char nod[MPI_MAX_PROCESSOR_NAME];
int n_l; MPI_Get_processor_name(nod, &n_l);
#pragma omp parallel for
for (int i = 0; i < n_omp; i++) {
rM8[i] = mal[i] + 10 * omp_get_thread_num();
printf("ws%2d\t\tmpi%2d\t\tmaxTh%2d\t\tmaxPr%2d\t\tomp%2d\t\tcore%3d\t\trM8%4d\t\tnod %s\n", ws, wr, omp_get_num_threads(), omp_get_num_procs(), omp_get_thread_num(), sched_getcpu(), rM8[i], nod);
}
return rM8;
}
int main(void) {
MPI_Init(NULL, NULL);
int ts[2] = {7, 9}; //no of processes
for (int t = 0; t < 2; t++) {
int ws = ts[t];
int errcodes[ws];
MPI_Comm parentcomm, intercomm;
MPI_Comm_get_parent(&parentcomm);
if (parentcomm == MPI_COMM_NULL) {
MPI_Comm_spawn("./b", MPI_ARGV_NULL, ws, MPI_INFO_NULL, 0, MPI_COMM_WORLD, &intercomm, errcodes);
//printf("I'm the parent.\n");
}
else {
int wr; MPI_Comm_rank(MPI_COMM_WORLD, &wr);// printf("wr %d\n", wr);
//int ps; MPI_Comm_size(parentcomm, &ps);// printf("ps %d\n", ps);
//int pr; MPI_Comm_rank(parentcomm, &pr);// printf("pr %d\n", pr);
int n_omp = 8, *mlu = NULL;
if (wr == 0) {
mlu = create_mlu(n_omp, ws);
//for (int i = 0; i < n_omp*ws; i++) printf("\tmlu[%2d] = %d\n", i, mlu[i]);
}
int *mal = (int *)malloc(n_omp * sizeof(int));
MPI_Scatter(mlu, n_omp, MPI_INT, mal, n_omp, MPI_INT, 0, MPI_COMM_WORLD);
//for (int i = 0; i < n_omp; i++) printf("\t\tmal[%2d] = %d\trank %d\n", i, mal[i], wr);
int *rM8 = NULL;
rM8 = C4_Re(mal, n_omp, wr, ws);
int *rS8 = NULL;
if (wr == 0)
rS8 = (int *)malloc(sizeof(int) * ws * n_omp);
MPI_Gather(rM8, n_omp, MPI_INT, rS8, n_omp, MPI_INT, 0, MPI_COMM_WORLD);
if (wr == 0) {
//for (int i = 0; i < n_omp * ws; i++) printf("\t\trS8[%2d] = %d\n", i, rS8[i]);
free(mlu);
free(rS8); }
free(mal);
free(rM8);
}
//fflush(stdout);
}
fflush(stdout);
MPI_Finalize();
return 0;
}
I have a memory corruption which I need help to find
Some results look like
ws 7 rM8-37253944 nod ma mpi 7 maxTh 6 maxPr 6 omp 4 core 4
but they must look like
ws 7 rM8 624 nod ma mpi 6 maxTh 6 maxPr 6 omp 2 core 2
addition questions
1 - why using parentcomm for Scatter and Gather is not correct? In my opinion parentcomm is the new communicator
2 - should I create different comunicators for 7 and 9?
3 - mpicc gives me wrong results I don't know why

MPI_Send works only with statically allocated buffer

If I want to define my own type, and use it as a datatype with MPI_Send to take only even rows from a matrix, does that matrix (send buffer) have to be allocated statically?
I seem to have problems when I allocate it dynamically. Is this because addresses need to be successive for data to be sent?
No, memory to be sent with MPI_Send does not have to be statically allocated.
To send array subsets, you likely want to use MPI_Type_indexed. Here is a slightly modified version of the example from the mpi.deino.net article on MPI_Type_indexed, where I have replaced the statically allocated buffer
int buffer[27];
to a dynamically allocated buffer
int* buffer = (int*)malloc(27 * sizeof(int));
I hope it helps:
#include <mpi.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int rank, size, i;
MPI_Datatype type, type2;
int blocklen[3] = { 2, 3, 1 };
int displacement[3] = { 0, 3, 8 };
int* buffer = (int*)malloc(27 * sizeof(int)); //int buffer[27];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (size < 2)
{
printf("Please run with 2 processes.\n");
MPI_Finalize();
return 1;
}
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Type_contiguous(3, MPI_INT, &type2);
MPI_Type_commit(&type2);
MPI_Type_indexed(3, blocklen, displacement, type2, &type);
MPI_Type_commit(&type);
if (rank == 0)
{
for (i=0; i<27; i++)
buffer[i] = i;
MPI_Send(buffer, 1, type, 1, 123, MPI_COMM_WORLD);
}
if (rank == 1)
{
for (i=0; i<27; i++)
buffer[i] = -1;
MPI_Recv(buffer, 1, type, 0, 123, MPI_COMM_WORLD, &status);
for (i=0; i<27; i++)
printf("buffer[%d] = %d\n", i, buffer[i]);
fflush(stdout);
}
MPI_Finalize();
free(buffer);
return 0;
}

MPI - C send 2D array of structures

I managed to write working code for sending one structure in MPI.But what i need is send two dimensional array of these structures and im stuck.
heres my code of sending one struct. Can you guide me how to modify it?
typedef struct {
unsigned char r;
unsigned char g;
unsigned char b;
} pixel;
MPI_Datatype mpi_pixel; /*datatype variable*/
pixel send_pixel; /*my instance of structure*/
int lengtharray[3]; /* Array of lengths */
MPI_Aint disparray[3]; /* Array of displacements */
MPI_Datatype typearray[3]; /* Array of MPI datatypes */
MPI_Aint startaddress, address;
lengtharray[0] = lengtharray[1] =lengtharray[2] = 1; /* Set array of lengths */
typearray[0] = typearray[1] = typearray[2]= MPI_UNSIGNED_CHAR;/* and data types */
/* First element, a, is at displacement 0 */
disparray[0] = 0;
/* Calculate displacement of b */
MPI_Address(&send_pixel.b, &startaddress);
MPI_Address(&send_pixel.g, &address);
disparray[1] = address-startaddress; /* Displacement of second element, b */
MPI_Address(&send_pixel.r, &address);
disparray[2] = address-startaddress; /* Displacement of third element, n */
/* Build the data structure my_type */
MPI_Type_struct(3, lengtharray, disparray, typearray, &mpi_pixel);
MPI_Type_commit(&mpi_pixel);
MPI_Send(&send_pixel, 1, mpi_pixel, 0, 50, MPI_COMM_WORLD);
There are a few different ways to send this. There are a few answers on SO that explain some of them, here, here.
If you want to continue on along the lines that you are doing, I'd create a contiguous type for a 1D array and then expand that with another contiguous type to a 2D array.
A BIG BIG WARNING I do not check for errors, you REALLY REALLY SHOULD.
I've changed you code around a bit, as I don't like using typedef's of structs. I also put the creation of the basic MPI pixel datatype into a function and added some test sending routines (of course you could extend them to pass in the pixel(s) you want to send:
/*
* Create a MPI datatype of a pixel.
*/
int
mpi_pixel_init(MPI_Datatype *mpi_pixel)
{
struct pixel_s pixel; /* instance of structure */
int i = 0; /* temporary loop indexer */
int count = 3; /* number of blocks in the struct */
int blocks[3] = {1, 1, 1}; /* set up 3 blocks */
MPI_Datatype types[3] = { /* pixel internal types */
MPI_UNSIGNED_CHAR,
MPI_UNSIGNED_CHAR,
MPI_UNSIGNED_CHAR
};
MPI_Aint dis[3] = { /* internal displacements */
offsetof(struct pixel_s, r),
offsetof(struct pixel_s, g),
offsetof(struct pixel_s, b)
};
MPI_Type_create_struct(count, blocks, dis, types, mpi_pixel);
MPI_Type_commit(mpi_pixel);
return(EXIT_SUCCESS);
}
Test sending a single pixel:
/* Send a single pixel */
int
send_pixel(int src, int dst, MPI_Datatype mpixel)
{
int rank = 0;
struct pixel_s x = {0};
MPI_Status stat;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == src) {
x.r = 255;
x.g = 128;
x.b = 128;
MPI_Send(&x, 1, mpixel, 1, 1, MPI_COMM_WORLD);
} else if (rank == dst) {
MPI_Recv(&x, 1, mpixel, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &stat);
printf("Single pixel\n");
printf("%d:\tr: %d\tg: %d\tb: %d\n", rank, x.r, x.g, x.b);
printf("----\n");
}
return(EXIT_SUCCESS);
}
Test sending a row of pixels:
/* Send a row/1D of pixels */
int
send_1d_pixels(int src, int dst, MPI_Datatype cpixel)
{
int i = 0;
int rank = 0;
struct pixel_s x[ROWS] = {0};
MPI_Status stat;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
/* Test sending a row of pixels */
if (rank == src) {
for (i = 0; i < ROWS; ++i) {
x[i].r = i;
x[i].g = i + 128;
x[i].b = 255 - i;
}
MPI_Send(&x, 1, cpixel, 1, TAG, MPI_COMM_WORLD);
} else if (rank == dst) {
MPI_Recv(&x, 1, cpixel, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &stat);
printf("Row of pixels\n");
for (i = 0; i < ROWS; ++i) {
printf("%d:\tr: %d\tg: %d\tb: %d\n", i,
x[i].r, x[i].g, x[i].b);
}
printf("----\n");
}
return(EXIT_SUCCESS);
}
Test sending a 2D array of pixels:
/* Send an 2D array of pixels */
int
send_2d_pixels(int src, int dst, MPI_Datatype apixel)
{
int i = 0;
int j = 0;
int rank = 0;
struct pixel_s x[ROWS][COLS] = {0};
MPI_Status stat;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
/* Test sending one pixel */
if (rank == src) {
for (i = 0; i < ROWS; ++i) {
for (j = 0; j < COLS; ++j) {
x[i][j].r = i;
x[i][j].g = j;
x[i][j].b = i*COLS + j;
}
}
MPI_Send(&x, 1, apixel, 1, TAG, MPI_COMM_WORLD);
} else if (rank == dst) {
MPI_Recv(&x, 1, apixel, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &stat);
printf("Array of pixels\n");
for (i = 0; i < ROWS; ++i) {
for (j = 0; j < COLS; ++j) {
printf("(%d,%d):\tr: %d\tg: %d\tb: %d\n", i, j,
x[i][j].r, x[i][j].g, x[i][j].b);
}
}
printf("----\n");
}
return(EXIT_SUCCESS);
}
Then later on you can use it as:
/*
* Create a 2D array of MPI pixels.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <mpi.h>
#define TAG 1
#define COLS 10
#define ROWS 10
struct pixel_s {
unsigned char r;
unsigned char g;
unsigned char b;
};
int mpi_pixel_init(MPI_Datatype *);
int send_pixel(int, int, MPI_Datatype);
int send_1d_pixels(int, int, MPI_Datatype);
int send_2d_pixels(int, int, MPI_Datatype);
int
main(int argc, char **argv)
{
MPI_Datatype mpixel; /* single pixel */
MPI_Datatype cmpixel; /* row/contiguous pixels */
MPI_Datatype ampixel; /* 2D array of pixels */
MPI_Init(&argc, &argv);
/* Create an MPI pixel datatype */
mpi_pixel_init(&mpixel);
/* Create a 1D array (contiguous) pixels */
MPI_Type_contiguous(ROWS, mpixel, &cmpixel);
MPI_Type_commit(&cmpixel);
/* Create a 2D array from a 1D array of pixels */
MPI_Type_contiguous(COLS, cmpixel, &ampixel);
MPI_Type_commit(&ampixel);
/* Test sending one pixel */
send_pixel(0, 1, mpixel);
/* Test sending a row of pixels */
send_1d_pixels(0, 1, cmpixel);
/* Test sending a 2D array of pixels */
send_2d_pixels(0, 1, ampixel);
/* Free up the types and finalize MPI */
MPI_Type_free(&ampixel);
MPI_Type_free(&cmpixel);
MPI_Type_free(&mpixel);
MPI_Finalize();
return(EXIT_SUCCESS);
}

Resources