#include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* The Inline::C source for this is available in the __DATA__ section of Simple.pm */ #include #define GATHER_TAG 11001 /* used to be unlikely to upset other sends */ /* root process first broadcasts length of stored data then broadcasts the data. Non-root processes receive length (via bcast), allocate space to take incomming data from root Both root and non-root processes then create and return a new scalar with contents identical to those root started with. */ SV* _Bcast (SV* data, int root, SV* comm) { int buf_len[1]; int rank; SV* rval; MPI_Comm_rank((MPI_Comm)SvIVX(comm), &rank); if (rank == root) { buf_len[0] = sv_len(data); MPI_Bcast(buf_len, 1, MPI_INT, root, (MPI_Comm)SvIVX(comm)); MPI_Bcast(SvPVX(data), buf_len[0], MPI_CHAR, root, (MPI_Comm)SvIVX(comm)); rval = newSVpvn(SvPVX(data), buf_len[0]); } else { char *recv_buf; MPI_Bcast(buf_len, 1, MPI_INT, root, (MPI_Comm)SvIVX(comm)); recv_buf = (char*)malloc((buf_len[0]+1)*sizeof(char)); if (recv_buf == NULL) croak("Allocation error in _Bcast"); MPI_Bcast(recv_buf, buf_len[0], MPI_CHAR, root, (MPI_Comm)SvIVX(comm)); rval = newSVpvn(recv_buf, buf_len[0]); free(recv_buf); } return rval; } /* Finds length of data in stor_ref, sends this to receiver, then sends actual data, uses same tag for each message. */ int _Send(SV* stor_ref, int dest, int tag, SV*comm) { int str_len[1]; str_len[0] = sv_len(stor_ref); MPI_Send(str_len, 1, MPI_INT, dest, tag, (MPI_Comm)SvIVX(comm)); MPI_Send(SvPVX(stor_ref), sv_len(stor_ref),MPI_CHAR, dest, tag, (MPI_Comm)SvIVX(comm)); return 0; } /* Receives int for length of data it should then expect, allocates space then receives data into that space. Creates a new SV and returns it. */ SV* _Recv (int source, int tag, SV*comm, SV*status) { MPI_Status tstatus; SV* rval; int len_buf[1]; char *recv_buf; MPI_Recv(len_buf, 1, MPI_INT, source, tag, (MPI_Comm)SvIVX(comm), &tstatus); recv_buf = (char*)malloc((len_buf[0]+1)*sizeof(char)); if (recv_buf == NULL) croak("Allocation error in _Recv"); MPI_Recv(recv_buf, len_buf[0], MPI_CHAR, source, tag, (MPI_Comm)SvIVX(comm), &tstatus); rval = newSVpvn(recv_buf, len_buf[0]); sv_setiv(status, tstatus.MPI_SOURCE); free(recv_buf); return rval; } /* Calls MPI_Init with dummy arguments, a bit dodgy but sort of ok */ int Init () { MPI_Init(&PL_origargc, &PL_origargv); } /* Returns rank of process within comm */ int _Comm_rank (SV* comm) { int trank; MPI_Comm_rank((MPI_Comm)SvIVX(comm),&trank); return trank; } /* returns total number of processes within comm */ int _Comm_size (SV* comm) { int tsize; MPI_Comm_size((MPI_Comm)SvIVX(comm), &tsize); return tsize; } /* returns SV whose IV slot is a cast pointer to the MPI_COMM_WORLD object */ SV* COMM_WORLD () { return newSViv((IV)MPI_COMM_WORLD); } /* returns SV whose IV slot is a cast pointer to the MPI_ANY_SOURCE value */ SV* ANY_SOURCE () { return newSViv((IV)MPI_ANY_SOURCE); } /* calls MPI_Barrier for comm */ int Barrier (SV*comm) { MPI_Barrier((MPI_Comm)SvIVX(comm)); } /* ends MPI participation */ int Finalize () { MPI_Finalize(); } /* If non-root: participates in Gather so that root finds length of data to expect from this process. Then send (using MPI_Send) data to root. If root: receives array of ints detailing length of scalars held by other processes, then receives from each in turn (using MPI_Recv) returns an array ref to root process only. */ SV* _Gather (SV* data, int root, SV* comm) { int rank, size, *buf_lens, i, max; char* recv_buf; int my_buf[1]; AV* ret_arr; MPI_Status tstatus; /* find out how long data is */ ret_arr = av_make(0,(SV**)NULL); my_buf[0] = sv_len(data); if (_Comm_rank(comm) == root) { MPI_Comm_size((MPI_Comm)SvIVX(comm), &size); buf_lens = malloc(size*sizeof(int)); if (buf_lens == NULL) croak("Allocation error (lens) in _Gather"); /* gather all scalar length data */ MPI_Gather(my_buf, 1, MPI_INT, buf_lens, 1, MPI_INT, root, (MPI_Comm)SvIVX(comm)); max = 0; // keep buffer allocation calls to minimum for (i=0;i