Related
My goal is to assign a processor to read a binary file into some storage for a matrix, then send it to the processor that matrix is assigned to for calculations. Then afterwards. This program would need to be able to accomodate a variable size of the matrix, and a variable number of processors.
This is all in C
The actual allocation for the matrix, in case im doing it wrong:
void alloc_matrix(
int nrows,
int ncols,
size_t element_size,
void **matrix_storage,
void ***matrix,
int *errvalue){
int i;
void *ptr_to_row_in_storage;
void **matrix_row_start;
const int MSG_TAG =1;
//1. Allocate array of full matrix
*matrix_storage = malloc(nrows *ncols * element_size);
//2. create 2d matrix based on nrows.
*matrix = malloc(nrows * sizeof(void*));
//3. put addresses in each row.
//get address of start of array of pointers to linear storage, which is address of (*matrix)[0]
matrix_row_start = (void*) &(*matrix[0]);
ptr_to_row_in_storage = (void*) *matrix_storage;
//for each matrix pointer, *matrix[i], i = 0... nrows-1,
//set it to start of the ith row in linear storage
for(i = 0; i<nrows; i++){
//matrix_row_start is address of *matrix[i] and ptr_to_row_in_storage is address of start of ith row.
//changes content of (*matrix)[i] to store the start of the ith row in linear storage.
*matrix_row_start = (void*) ptr_to_row_in_storage;
matrix_row_start++;
ptr_to_row_in_storage += ncols * element_size;
}
*errvalue = SUCCESS;
}
The code for reading the file into the matrices to be sent:
void read_and_distribute_matrix(
char *filename,
void ***matrix, //matrix to fill
void **matrix_storage, //linear storage for matrix
MPI_Datatype dtype,
int *nrows, // rows in matrix
int *ncols, // columns in matrix
MPI_Comm comm
){
...
if(p-1 == id){
int nrows_to_send;
int num_elements;
size_t nelements_read;
int i;
//for process i go up to p-2 and send chunk of bytes to process i from p-1.
for(int i=0; i< p-1; i++){
nrows_to_send = number_of_rows (i, *nrows, p);
num_elements = nrows_to_send * (*ncols);
nelements_read = fread(*matrix_storage, element_size,
num_elements, file);
//check number read
MPI_Send (*matrix_storage, num_elements, dtype,
i, MSG_TAG, comm);
}
nelements_read = fread(*matrix_storage, element_size,
nlocal_rows * (*ncols), file);
fclose(file);
}
else{
MPI_Recv( *matrix_storage, nlocal_rows * (*ncols),
dtype, p-1, MSG_TAG, comm, &status);
}
So the last processor would be in charge with reading the file, and sending the read data to the appropriate processor, using MPI_Send. Then the processor, using MPI_Recv, obtains the data and places it in its local matrix_storage. But when I try to print the matrices, I receive this message:
*** An error occurred in MPI_Recv
*** reported by process
*** on communicator MPI_COMM_WORLD
*** MPI_ERR_TAG: invalid tag
*** MPI_ERRORS_ARE_FATAL (processes in this communicator will now abort,
*** and potentially your MPI job)
1 more process has sent help message help-mpi-errors.txt / mpi_errors_are_fatal
Set MCA parameter "orte_base_help_aggregate" to 0 to see all help / error messages
As I am still a novice in MPI, and I have identified the error to come from MPI_Send, but not sure how to fix it. I have tried changing the method of allocation to disastrous results, or comparing the matrix differently. Is there an area I should look at?
Thanks in advance.
Hello I have a file containing that kind of data lines:
AsfAGHM5om 00000000000000000000000000000000 0000222200002222000022220000222200002222000000001111
I want to read this kind of data and send them over using C and MPI. So I've reached the following C code:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // used for offsetof
typedef struct tuple_str{
char *key;
char *index;
char *value;
} tuple;
int main(int argc, char** argv) {
// Initialize the MPI environment
MPI_Init(&argc, &argv);
// Initialize file pointer
FILE *fp = fopen("tuples","r");
// define original structure that stores file and temp used by each process
tuple A[10000],B[10000];
// mpi structure name
MPI_Datatype mpi_tuples_str;
// number of structure members
const int nitems = 3;
// array of structure member sizes
int blocklengths[3];
blocklengths[0] = sizeof(A->key);
blocklengths[1] = sizeof(A->index);
blocklengths[2] = sizeof(A->value);
// structure member types
MPI_Datatype types[3] = {MPI_CHAR,MPI_CHAR,MPI_CHAR};
// status
MPI_Status status;
// offset of structure members
MPI_Aint offsets[3];
offsets[0] = offsetof(tuple,key);
offsets[1] = offsetof(tuple,index);
offsets[2] = offsetof(tuple,value);
// create mpi struct
MPI_Type_create_struct(nitems,blocklengths, offsets, types, &mpi_tuples_str);
MPI_Type_commit(&mpi_tuples_str);
// Get the number of processes
int size;
MPI_Comm_size(MPI_COMM_WORLD, &size);
// Get the rank of the process
int my_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
int index = 0;
int i;
int local_A_size = (10000%size == 0) ? 10000/size : 0;
if ( my_rank == 0){
char text[10000];
char *p;
p=strtok(NULL," ");
// node0 reads file form hard drive and saves file to struct
while( fgets(text,10000,fp)!=NULL){
p = strtok (text," ");
char *temp[3];
temp[0]=p;
A[index].key=temp[0];
p = strtok (NULL, " ");
temp[1] = p;
A[index].index=temp[1];
p = strtok (NULL, " ");
temp[2] = p;
A[index].value=temp[2];
// printf("%s ",A[index].key);
// printf("%s ",A[index].index);
// printf("%s\n",A[index].value);
index++;
}
fclose(fp);
}
if ( local_A_size != 0){
if (my_rank == 0) {
printf("File saved to memory of process %d!\n",my_rank);
printf("Process %d sending struct data to others...\n",my_rank);
}
// send struct to all processes
MPI_Scatter(&A,local_A_size,mpi_tuples_str,B,local_A_size,mpi_tuples_str,0,MPI_COMM_WORLD);
// MPI_Bcast(&A,index,mpi_tuples_str,0,MPI_COMM_WORLD);
for(i=0;i<=local_A_size;i++)
printf("I'm process %d and my result is: %s\n",my_rank,B[i].key);
if (my_rank == 0) printf("Data sent from process %d to others...\n",my_rank);
}
else
{
if (my_rank == 0) printf("Number of processes must be an exact divisor of %d, %d in not %ds divisor\n",index,size,index);
}
// free memory used by mpi_tuples_str
MPI_Type_free(&mpi_tuples_str);
// Finalize
MPI_Finalize();
return 0;
}
So the problem here is, as far as I can understand_ the creation and allocation of memory of my struct at first and the packing and sending of it at second.
As you can see I've tried both MPI_Scatter & MPI_Bcast but none of them helped me.
The result is that, as it supposed to, process 0 that reads the file has data but all others not. Also I'm getting this weird message of
= BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES
= EXIT CODE: 11
= CLEANING UP REMAINING PROCESSES
= YOU CAN IGNORE THE BELOW CLEANUP MESSAGES
I'll be so grateful if someone can enlighten me!!
Alright I 've changed my code to the following:
`#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // used for offsetof
typedef struct tuple_str{
char key[10];
char index[12];
char value[52];
} tuple;
int main(int argc, char** argv) {
// Initialize the MPI environment
MPI_Init(NULL, NULL);
// Initialize file pointer
FILE *fp = fopen("tuples_mini","r");
// define original structure that stores file and temp used by each process
tuple A[10000],B[10000];
// mpi structure name
MPI_Datatype mpi_tuples_str;
// number of structure members
const int nitems = 3;
// array of structure member sizes
int blocklengths[3];
blocklengths[0] = sizeof(10);
blocklengths[1] = sizeof(12);
blocklengths[2] = sizeof(52);
// structure member types
MPI_Datatype types[3] = {MPI_CHAR,MPI_CHAR,MPI_CHAR};
// status
MPI_Status status;
// offset of structure members
MPI_Aint offsets[3];
offsets[0] = offsetof(tuple,key);
offsets[1] = offsetof(tuple,index);
offsets[2] = offsetof(tuple,value);
// create mpi struct
MPI_Type_create_struct(nitems,blocklengths, offsets, types, &mpi_tuples_str);
MPI_Type_commit(&mpi_tuples_str);
// Get the number of processes
int size;
MPI_Comm_size(MPI_COMM_WORLD, &size);
// Get the rank of the process
int my_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
int index = 0;
int i;
int local_A_size = (10000%size == 0) ? 10000/size : 0;
char *tmp[10000],*b[10000];
if ( my_rank == 0){
char text[10000];
char *p;
p=strtok(NULL," ");
// node0 reads file form hard drive and saves file to struct
while( fgets(text,10000,fp)!=NULL){
p = strtok (text," ");
char *temp[3];
temp[0]=p;
strcpy(A[index].key,temp[0]);
p = strtok (NULL, " ");
temp[1] = p;
strcpy(A[index].index,temp[1]);
p = strtok (NULL, " ");
temp[2] = p;
strcpy(A[index].value,temp[2]);
printf("%s ",A[index].key);
printf("%s ",A[index].index);
printf("%s\n",A[index].value);
index++;
}
fclose(fp);
}
if ( local_A_size != 0){
if (my_rank == 0) {
printf("File saved to memory of process %d!\n",my_rank);
printf("Process %d sending struct data to others...\n",my_rank);
}
// send struct to all processes
MPI_Scatter(&A,index,mpi_tuples_str,B,index,mpi_tuples_str,0,MPI_COMM_WORLD);
// MPI_Bcast(&tmp,index,MPI_CHAR,0,MPI_COMM_WORLD);
for(i=0;i<=local_A_size;i++){
// MPI_Recv(&tmp,index,MPI_CHAR,0,10,MPI_COMM_WORLD,&status);
printf("I'm process %d and my result is: %s\n",my_rank,B[i].key);
}
if (my_rank == 0) printf("Data sent from process %d to others...\n",my_rank);
}
else
{
if (my_rank == 0) printf("Number of processes must be an exact divisor of %d, %d in not %ds divisor\n",index,size,index);
}
// free memory used by mpi_tuples_str
MPI_Type_free(&mpi_tuples_str);
// Finalize
MPI_Finalize();
return 0;
}`
but that lead me to new error:
============================================================================== =====
= BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES
= EXIT CODE: 6
= CLEANING UP REMAINING PROCESSES
= YOU CAN IGNORE THE BELOW CLEANUP MESSAGES
============================================================================== =====
YOUR APPLICATION TERMINATED WITH THE EXIT STRING: Aborted (signal 6)
This typically refers to a problem with your application.
Please see the FAQ page for debugging suggestions
after last suggestions!
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // used for offsetof
typedef struct tuple_str{
char key[10];
char index[12];
char value[52];
} tuple;
int main(int argc, char** argv) {
// Initialize the MPI environment
MPI_Init(NULL, NULL);
// Initialize file pointer
FILE *fp = fopen("tuples_mini","r");
// define original structure that stores file and temp used by each process
tuple A[10000],B[10000];
// mpi structure name
MPI_Datatype mpi_tuples_str;
// number of structure members
const int nitems = 3;
// array of structure member sizes
int blocklengths[3];
blocklengths[0] = 11;
blocklengths[1] = 33;
blocklengths[2] = 53;
// structure member types
MPI_Datatype types[3] = {MPI_CHAR,MPI_CHAR,MPI_CHAR};
// status
MPI_Status status;
// offset of structure members
MPI_Aint offsets[3];
offsets[0] = offsetof(tuple,key);
offsets[1] = offsetof(tuple,index);
offsets[2] = offsetof(tuple,value);
// create mpi struct
MPI_Type_create_struct(nitems,blocklengths, offsets, types, &mpi_tuples_str);
MPI_Type_commit(&mpi_tuples_str);
// Get the number of processes
int size;
MPI_Comm_size(MPI_COMM_WORLD, &size);
// Get the rank of the process
int my_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
int index = 0;
int i;
int local_A_size = (10000%size == 0) ? 10000/size : 0;
char *tmp[10000],*b[10000];
if ( my_rank == 0){
char text[10000];
char *p;
// p=strtok(NULL," ");
// node0 reads file form hard drive and saves file to struct
while( fgets(text,10000,fp) != NULL && fp != NULL){
p = strtok (text," ");
char *temp[3];
temp[0]=p;
strcpy(A[index].key,temp[0]);
p = strtok (NULL, " ");
temp[1] = p;
strcpy(A[index].index,temp[1]);
p = strtok (NULL, " ");
temp[2] = p;
strcpy(A[index].value,temp[2]);
printf("%s ",A[index].key);
printf("%s ",A[index].index);
printf("%s\n",A[index].value);
tmp[index] = temp[0];
// printf("%s\n",tmp[index]);
index++;
}
fclose(fp);
}
if ( local_A_size != 0){
if (my_rank == 0) {
printf("File saved to memory of process %d!\n",my_rank);
printf("Process %d sending struct data to others...\n",my_rank);
// MPI_Send(&A,index,mpi_tuples_str,0,10,MPI_COMM_WORLD);
}
// send struct to all processes
MPI_Scatter(&A,index,mpi_tuples_str,B,index,mpi_tuples_str,0,MPI_COMM_WORLD);
// MPI_Bcast(&tmp,index,MPI_CHAR,0,MPI_COMM_WORLD);
// MPI_Bcast(&A,index,mpi_tuples_str,0,MPI_COMM_WORLD);
for(i=0;i<=local_A_size;i++){
// MPI_Recv(&tmp,index,MPI_CHAR,0,10,MPI_COMM_WORLD,&status);
// MPI_Recv(&A,index,mpi_tuples_str,0,10,MPI_COMM_WORLD,&status);
printf("I'm process %d and my result is: %s\n",my_rank,B[i].key);
}
if (my_rank == 0) printf("Data sent from process %d to others...\n",my_rank);
}
else
{
if (my_rank == 0) printf("Number of processes must be an exact divisor of %d, %d in not %ds divisor\n",index,size,index);
}
// free memory used by mpi_tuples_str
MPI_Type_free(&mpi_tuples_str);
// Finalize
MPI_Finalize();
return 0;
}
I applied all the comments about problems in the posted code, this is the result:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // used for offsetof
typedef struct tuple_str
{
char key[11];
char index[33];
char value[53];
} tuple;
int main( void )
{
// Initialize the MPI environment
MPI_Init( NULL, NULL );
// Initialize file pointer
FILE *fp = NULL;
if( NULL == ( fp = fopen( "tuples_mini" ,"r" ) ) )
{
perror( "fopen for read of truples_mini failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
// define original structure that stores file and temp used by each process
tuple A[10000];
tuple B[10000];
// mpi structure name
MPI_Datatype mpi_tuples_str;
// number of structure members
const int nitems = 3;
// array of structure member sizes
int blocklengths[3];
blocklengths[0] = 11;
blocklengths[1] = 33;
blocklengths[2] = 53;
// structure member types
MPI_Datatype types[3] = { MPI_CHAR, MPI_CHAR, MPI_CHAR };
// status
//MPI_Status status;
// offset of structure members
MPI_Aint offsets[3];
offsets[0] = offsetof( tuple,key);
offsets[1] = offsetof( tuple,index);
offsets[2] = offsetof( tuple,value);
// create mpi struct
MPI_Type_create_struct( nitems, blocklengths, offsets, types, &mpi_tuples_str);
MPI_Type_commit( &mpi_tuples_str);
// Get the number of processes
int size;
MPI_Comm_size( MPI_COMM_WORLD, &size);
// Get the rank of the process
int my_rank;
MPI_Comm_rank( MPI_COMM_WORLD, &my_rank);
int index = 0;
int i;
int local_A_size = (10000%size == 0) ? 10000/size : 0;
//char *tmp[10000];
//char *b[10000];
if ( my_rank == 0)
{
char text[10000];
char *p;
//p=strtok(NULL," ");
// node0 reads file from hard drive and saves file to struct
while( fgets( text, sizeof text, fp ) )
{
p = strtok (text," ");
char *temp[3];
temp[0]=p;
strcpy( A[index].key,temp[0]);
p = strtok (NULL, " ");
temp[1] = p;
strcpy( A[index].index,temp[1]);
p = strtok (NULL, " ");
temp[2] = p;
strcpy( A[index].value,temp[2]);
printf( "%s ",A[index].key);
printf( "%s ",A[index].index);
printf( "%s\n",A[index].value);
index++;
}
fclose(fp);
}
if ( local_A_size != 0)
{
if (my_rank == 0)
{
printf( "File saved to memory of process %d!\n",my_rank);
printf( "Process %d sending struct data to others...\n",my_rank);
}
// send struct to all processes
MPI_Scatter( &A,index, mpi_tuples_str, B, index, mpi_tuples_str, 0, MPI_COMM_WORLD;
// MPI_Bcast( &tmp,index, MPI_CHAR, 0, MPI_COMM_WORLD);
for(i=0;i<=local_A_size;i++)
{
// MPI_Recv( &tmp, index, MPI_CHAR, 0, 10, MPI_COMM_WORLD, &status);
printf( "I'm process %d and my result is: %s\n", my_rank, B[i].key);
}
if (my_rank == 0)
printf("Data sent from process %d to others...\n", my_rank);
}
else
{
if (my_rank == 0)
printf( "Number of processes must be an exact divisor of %d, %d in not %ds divisor\n", index, size,index);
}
// free memory used by mpi_tuples_str
MPI_Type_free( &mpi_tuples_str);
// Finalize
MPI_Finalize();
return 0;
}
I setup the tuples_mini file to contain:
AsfAGHM5om 00000000000000000000000000000000 0000222200002222000022220000222200002222000000001111
when I ran the program on my ubuntu linux 14.04 with 4 core processor, this is the output:
AsfAGHM5om 00000000000000000000000000000000 0000222200002222000022220000222200002222000000001111
File saved to memory of process 0!
Process 0 sending struct data to others...
I'm process 0 and my result is: AsfAGHM5om
I'm process 0 and my result is:
then several dozens of this line:
I'm process 0 and my result is:
followed by these lines:
I'm process 0 and my result is:
I'm process 0 and my result is: AsfAGHM5om
Data sent from process 0 to others...
so there seems to be a logic problem in the code, but it does not seg fault
on the second version of the posted code
The most likely cause of the signal 6/sigabort is because the first call to strtok() has NULL as the first parameter rather than the address of a visible buffer.
IMO: that first call to strtok() should be completely removed from the program.
I'm trying to send a struct which has one of the member as a dynamic array, but this array doesn't seem to be sent properly. Any suggestion on how to do this?
This is what I have:
struct bar
{
int a;
int b;
int* c;
};
void defineMPIType(MPI_Datatype* newType, int cLen, struct bar* msg)
{
int blockLengths[3] = {1, 1, cLen};
MPI_Datatype types[3] = {MPI_INT, MPI_INT, MPI_INT};
MPI_Aint offsets[3];
MPI_Aint addrB, addrC;
MPI_Address(&(msg->b), &addrB);
MPI_Address(msg->c, &addrC);
offsets[0] = offsetof(struct bar, a);
offsets[1] = offsetof(struct bar, b);
offsets[2] = addrC - addrB;
MPI_Type_create_struct(3, blockLengths, offsets, types, newType);
MPI_Type_commit(newType);
}
void main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
int rank, p;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &p);
int cLen = argv[0];
MPI_Datatype MPI_BAR_TYPE;
struct bar* msg = malloc(sizeof(*msg));
msg->c = malloc(sizeof(int) * cLen);
defineMPIType(&MPI_BAR_TYPE, cLen, msg);
if (rank == 0)
{
msg->a = 1;
msg->b = 2;
for (int i = 0; i < cLen; ++i)
msg->c[i] = i;
MPI_Send(msg, 1, MPI_BAR_TYPE, 1, 111, MPI_COMM_WORLD);
}
else
{
MPI_Status stat;
MPI_Recv(msg, 1, MPI_BAR_TYPE, 0, 111, MPI_COMM_WORLD, &stat);
}
printf("Rank %d has c = [", rank);
for (int i = 0; i < cLen; ++i)
printf("%d, ", msg->c[i]);
printf("]\n");
free(msg);
MPI_Type_free(&MPI_BAR_TYPE);
MPI_Finalize();
}
Members a and b got sent properly, but c didn't.
There are a few issues in your code, even ignoring the issue of the type itself:
The first one is that you allocated memory for your c array only on process #0, then you (tried to) send this data to process #1. But process #1 didn't allocate any memory for storing the message. So even if the way the sending is done was correct, the code would have failed.
Names starting with MPI_ are reserved for the MPI library so you cannot use them as you wish. You have to find another name for your MPI_BAR_TYPE.
This line puzzles me somewhat: int cLen = argv[0]; I imagine you want to read from the command line the size of the array to allocate, in which case maybe that should read something like int clen = atoi(argv[1]); (forgetting about test for validity of this which would need to be properly handled...)
You only test if the process is of rank #0 or not, meaning that if for some reason you launched 3 processes, the process of rank #2 will wait forever for a message from process of rank #0 that will never arrive.
And finally the array itself: in your code there is a big confusion between the pointer c and the data pointed to by c. Your structure embeds the pointer, but not the memory pointed to. So you cannot map into an MPI structure the corresponding data... The most obvious reason is that from one call to the next (or from one process to the next), there is no guaranty that the offset from the address of the structure and the address of the data pointed to by c will be identical (and indeed, it is almost guaranteed it will be different). So you cannot reliably map them.
What you need to do for solving your problem is therefore to only transfer your 2 integers a and b in one go (possibly creating a MPI structure for transferring arrays of them if needed). Then you will transfer the memory pointed by c, which you would have allocated beforehand.
Your code could become for example:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
struct bar
{
int a;
int b;
int* c;
};
void defineMPIType( MPI_Datatype* newType ) {
struct bar tmp[2];
MPI_Aint extent = &tmp[1] - &tmp[0];
MPI_Type_create_resized( MPI_2INT, 0, extent, newType );
MPI_Type_commit( newType );
}
int main( int argc, char* argv[] ) {
MPI_Init(&argc, &argv);
int rank, p;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &p );
int cLen = atoi( argv[1] );
MPI_Datatype Bar_type;
defineMPIType( &Bar_type );
struct bar msg;
msg.c = ( int* ) malloc( sizeof( int ) * cLen );
if ( rank == 0 ) {
msg.a = 1;
msg.b = 2;
for ( int i = 0; i < cLen; ++i ) {
msg.c[i] = i;
}
MPI_Send( &msg, 1, Bar_type, 1, 111, MPI_COMM_WORLD );
MPI_Send( msg.c, cLen, MPI_INT, 1, 222, MPI_COMM_WORLD );
}
else if ( rank == 1 ) {
MPI_Recv( &msg, 1, Bar_type, 0, 111, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
MPI_Recv( msg.c, cLen, MPI_INT, 0, 222, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
}
printf("Rank %d has a = %d, b = %d, c = [", rank, msg.a, msg.b );
for ( int i = 0; i < cLen - 1; ++i ) {
printf( "%d, ", msg.c[i] );
}
printf( "%d]\n", msg.c[cLen - 1] );
free( msg.c );
MPI_Type_free( &Bar_type );
MPI_Finalize();
return 0;
}
Which gives:
$ mpirun -n 2 ./a.out 3
Rank 0 has a = 1, b = 2, c = [0, 1, 2]
Rank 1 has a = 1, b = 2, c = [0, 1, 2]
Happy MPI coding.
For my Parallel Computing class, I am working on a project that parallelizes the Game of Life using MPI. I am specifically implementing exercise 6.13 in "Parallel Programming in C with MPI and OpenMP" by Michael J. Quinn.
I am using the author's pre-written library function, "read_row_striped_matrix". The following is the code for the function:
/*
* Process p-1 opens a file and inputs a two-dimensional
* matrix, reading and distributing blocks of rows to the
* other processes.
*/
void read_row_striped_matrix (
char *s, /* IN - File name */
void ***subs, /* OUT - 2D submatrix indices */
void **storage, /* OUT - Submatrix stored here */
MPI_Datatype dtype, /* IN - Matrix element type */
int *m, /* OUT - Matrix rows */
int *n, /* OUT - Matrix cols */
MPI_Comm comm) /* IN - Communicator */
{
int datum_size; /* Size of matrix element */
int i;
int id; /* Process rank */
FILE *infileptr; /* Input file pointer */
int local_rows; /* Rows on this proc */
void **lptr; /* Pointer into 'subs' */
int p; /* Number of processes */
void *rptr; /* Pointer into 'storage' */
MPI_Status status; /* Result of receive */
int x; /* Result of read */
MPI_Comm_size (comm, &p);
MPI_Comm_rank (comm, &id);
datum_size = get_size (dtype);
/* Process p-1 opens file, reads size of matrix,
and broadcasts matrix dimensions to other procs */
if (id == (p-1)) {
infileptr = fopen (s, "r");
if (infileptr == NULL) *m = 0;
else {
fread (m, sizeof(int), 1, infileptr);
fread (n, sizeof(int), 1, infileptr);
}
}
MPI_Bcast (m, 1, MPI_INT, p-1, comm);
if (!(*m)) MPI_Abort (MPI_COMM_WORLD, OPEN_FILE_ERROR);
MPI_Bcast (n, 1, MPI_INT, p-1, comm);
local_rows = BLOCK_SIZE(id,p,*m);
/* Dynamically allocate matrix. Allow double subscripting
through 'a'. */
*storage = (void *) my_malloc (id,
local_rows * *n * datum_size);
*subs = (void **) my_malloc (id, local_rows * PTR_SIZE);
lptr = (void *) &(*subs[0]);
rptr = (void *) *storage;
for (i = 0; i < local_rows; i++) {
*(lptr++)= (void *) rptr;
rptr += *n * datum_size;
}
/* Process p-1 reads blocks of rows from file and
sends each block to the correct destination process.
The last block it keeps. */
if (id == (p-1)) {
for (i = 0; i < p-1; i++) {
x = fread (*storage, datum_size,
BLOCK_SIZE(i,p,*m) * *n, infileptr);
MPI_Send (*storage, BLOCK_SIZE(i,p,*m) * *n, dtype,
i, DATA_MSG, comm);
}
x = fread (*storage, datum_size, local_rows * *n,
infileptr);
fclose (infileptr);
} else
MPI_Recv (*storage, local_rows * *n, dtype, p-1,
DATA_MSG, comm, &status);
}
In the beginning of my code, I call "read_row_striped_matrix" like this:
#include <stdio.h>
#include <mpi.h>
#include "MyMPI.h"
typedef int dtype;
#define MPI_TYPE MPI_INT
int main(int argc, char *argv[]) {
dtype** matrix; /* Doubly-subscripted array */
dtype* storage; /* Local portion of array elements */
int proc_id; /* Process Rank */
int row_count; /* Number of rows in matrix */
int col_count; /* Number of columns in matrix */
int proc_count; /* Number of processes */
int i; /* Used with for loop */
MPI_Init (&argc, &argv);
MPI_Comm_rank (MPI_COMM_WORLD, &proc_id);
MPI_Comm_size (MPI_COMM_WORLD, &proc_count);
read_row_striped_matrix (argv[3], (void *) &matrix, (void *) &storage, MPI_TYPE,
&row_count, &col_count, MPI_COMM_WORLD);
....
The problem is, my implementation was getting stuck in an infinite loop. So I started debugging by testing to see if the data was being read from the text file correctly. My text file named "file_input.txt" contains the following input, where the first number (5) represents the number of rows, and the second number (also 5) represents the number of cols, and the rest of the data are the values in the matrix:
5 5 0 0 1 0 1 0 0 1 ...
I inserted the following printf statements in the section of library code where the length and height was being read from the text file:
if (id == (p-1))
printf("The name of the file is %s\n", s);
infileptr = fopen (s, "r");
if (infileptr == NULL) *m = 0;
else {
printf("The value of m is %d\n", *m);
size_t ret_val = fread (m, sizeof(int), 1, infileptr);
size_t next_ret_val = fread (n, sizeof(int), 1, infileptr);
printf("The total # of elements successfully read is: %d\n", ret_val);
printf("The total # of elements successfully read is: %d\n", next_ret_val);
printf("The value of m is %d\n", *m);
printf("The value of n is %d\n", *n);
}
}
For executing "project_3 5 5 file_input.txt", The output of the program is:
The name of the file is: file_input.txt
The value of m is 0
The total number of elements successfully read is: 1
The total number of elements successfully read is: 1
The value of m is: 540549176
The value of n is: 540090416
...
From what I observe, the name of the file was read in correctly, and the value of m (0) is correct before calling fread. fread is reading in the correct # of elements for both m and n, but the values are "540549176" and "540090416" instead of 5 and 5. When I try changing the numbers in the beginning of the text file to say, "3 and 4" for example, the value of m and n does not change.
Does anybody have any idea why the first two integers are not being read in from the text file correctly? Thanks in advance.
You have two options here:
this program is expecting binary input. so you need to produce binary input somehow. "5" is an ascii character with the hex value 0x35 (decimal 53). When you fread sizeof(int), you are actually going to pull in 2 characters.
you can edit the program to parse ascii text but this is kind of annoying. First you read in a line of the file, then you tokenize it, then you convert each token into integers. Are you coming from a perl/python background? This text conversion stuff is nearly automatic in scripting languages. nothing is automatic in C
Look you need to rip the library function off and rewrite it to read and interpret text files. At present it reads binary data, when it reads into m and n it reads sizeof(int) (probably 4 bytes) of hexadecimal data ie 00,00,00,05 is what's expected to be in your file but the first 4 bytes are something like 76,32,76,32
Rather than rewrite the library function it probably makes sense to write a file compiler that reads a text file as input and writes it as it's binary data
i have an this input file .txt where there are sequences:
NAMEOFSEQUENCE1/SEQUENCE1
NAMEOFSEQUENCE2/SEQUENCE2
NAMEOFSEQUENCE3/SEQUENCE3
I done a struct:
typedef struct lane{
char *name;
char *sequence;
}lane;
and wrote this code:
int i=0;
lane* toSend = malloc(sizeof(*toSend)*3);
while (fgets(line,strlen(line),fileinput) != NULL){
//GETTING NAME AND SEQUENCE, LINE PER LINE
char *tempName = malloc(strlen(line)-strlen(strstr(line,"\\"))+1);
strncpy(tempName,line,strlen(line)-strlen(strstr(line,"\\")));
tempName[strlen(line)-strlen(strstr(line,"\\"))] = '\0';
char *tempSequence = malloc(strlen(strstr(line,"\\")));
strncpy(tempSequence,strstr(line,"\\")+1,strlen(strstr(line,"\\")));
tempSequence[strlen(strstr(line,"\\"))-1] = '\0';
//FILLING TOSEND
toSend[i].name = malloc(strlen(line)-strlen(strstr(line,"\\"))+1);
toSend[i].sequence = malloc(strlen(strstr(line,"\\")));
howmuchbyte += strlen(line)+1;
strcpy(toSend[i].name,tempName);
strcpy(toSend[i].sequence,tempSequence);
i++;
}
I put in "line" variable one line of the file at a time and put in tempName the NAMEOFSEQUENCEX of each sequence and put in tempSequence the SEQUENCEX.
At this point all is working. If i Print the "toSend" vector i get the right value!
So i wrote this:
MPI_Send(toSend, 3, MPI_BYTE, 1, tag, MPI_COMM_WORLD);
so i send from process with rank 0 to process with rank 1 (i have 2 process). I put 3 as count parameter because i have 3 elements in the array.
The process with rank 1 do this:
lane* received = malloc(sizeof(*received)*3);
MPI_Recv(received, 3, MPI_BYTE, 0, tag, MPI_COMM_WORLD, &status);
If i do this on process with rank 1:
printf("%s",received[0].name);
i get a segmentation fault. What do i wrong?
This could work if your strings are of fixed maximum length, e.g.
typedef struct lane{
char name[NAME_MAX];
char sequence[SEQ_MAX];
}lane;
In that case you can simply define a new MPI structured datatype and use it in both send and receive operations:
int blens[2] = { NAME_MAX, SEQ_MAX };
int disps[2] = { offsetof(lane, name), offsetof(lane, sequence) };
int oldtypes[2] = { MPI_CHAR, MPI_CHAR };
MPI_Datatype type_lane;
MPI_Type_create_struct(2, blens, disps, oldtypes, &type_lane);
MPI_Type_commit(&type_lane);
lane aLane[2];
if (rank == 0)
{
strncpy(aLane[0].name, NAME_MAX, "foo1");
strncpy(aLane[0].sequence, SEQ_MAX, "bar");
strncpy(aLane[1].name, NAME_MAX, "foo2");
strncpy(aLane[1].sequence, SEQ_MAX, "baz");
MPI_Send(aLane, 2, type_lane, 1, tag, MPI_COMM_WORLD);
}
else if (rank == 1)
{
MPI_Recv(aLane, 2, type_lane, 0, tag, MPI_COMM_WORLD, &status);
}
If your strings are of strongly varying lengths, then you should serialize each structure before sending it. The most simple thing that comes to my mind is to just concatenate all name/sequence pairs, separated by a NUL:
int total_length = 0;
for (i = 0; i < num_to_send; i++)
total_length += strlen(toSend[i].name) + strlen(toSend[i].sequence) + 2;
char *bigstr = malloc(total_length);
char *cur = bigstr;
for (i = 0; i < num_to_send; i++)
{
strcpy(cur, toSend[i].name);
cur += strlen(toSend[i].name) + 1;
strcpy(cur, toSend[i].sequence);
cur += strlen(toSend[i].sequence) + 1;
}
Now the content of bigstr is as follows:
toSend[0].name \0 toSend[0].sequence \0 toSend[1].name \0 toSend[1].sequence \0 ....
The sender can now send the string and dispose it:
MPI_Send(bigstr, total_length, MPI_CHAR, 1, tag, MPI_COMM_WORLD);
The receiver has to be prepared to receive a message of unknown size. That can be achieved by first calling MPI_Probe and then MPI_Recv:
MPI_Status;
MPI_Probe(1, tag, MPI_COMM_WORLD, &status);
MPI_Get_count(&status, MPI_CHAR, &total_length);
char *bigstr = malloc(total_length);
MPI_Recv(bigstr, total_length, MPI_CHAR, 1, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
Now comes the part where you have to deserialise the big string into a collection of tuples. One way to do it is to first walk it and count the number of NULs and divide them by two. Then walk it again and copy each item to the corresponding location:
int num_structs = 0;
for (i = 0; i < total_length; i++)
if (bigstr[i] == '\0') num_structs++;
num_structs /= 2;
lane *lanes = malloc(num_structs * sizeof(lane));
char *cur = bigstr;
for (i = 0; i < num_structs; i++)
{
lanes[i].name = strdup(cur);
cur += strlen(cur);
lanes[i].sequence = strdup(cur);
cur += strlen(cur);
}
Another possible solution would be to utilise MPI_Pack and MPI_Unpack instead.
You can't just send raw pointers across an MPI channel. Well, you can, but the process that receives a pointer from some other process won't have the same data at the memory location (in their own memory space) referred to by the pointer.
If you want to send a variably-sized array (such as a string) through MPI, you'll need to test for the size of the array first, and allocate an appropriately sized array on the receiving end.
For more info: How to send and receive string using MPI