static char rcsid[] = "$Id: pvmgsu_aux.c,v 1.10 2007/04/19 23:15:33 pvmsrc Exp $"; /* * PVM version 3.4: Parallel Virtual Machine System * University of Tennessee, Knoxville TN. * Oak Ridge National Laboratory, Oak Ridge TN. * Emory University, Atlanta GA. * Authors: J. J. Dongarra, G. E. Fagg, M. Fischer * G. A. Geist, J. A. Kohl, R. J. Manchek, P. Mucci, * P. M. Papadopoulos, S. L. Scott, and V. S. Sunderam * (C) 1997 All Rights Reserved * * NOTICE * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted * provided that the above copyright notice appear in all copies and * that both the copyright notice and this permission notice appear in * supporting documentation. * * Neither the Institutions (Emory University, Oak Ridge National * Laboratory, and University of Tennessee) nor the Authors make any * representations about the suitability of this software for any * purpose. This software is provided ``as is'' without express or * implied warranty. * * PVM version 3 was funded in part by the U.S. Department of Energy, * the National Science Foundation and the State of Tennessee. */ /* * pvmgsu_aux.c - auxiliary group library routines, gather and scatter * 6 Jun 1995 Native mode reduce for Paragon. Donato * 8 Mar 1994 Added reduce & assoc routines. Donato & P.Papadopoulos * 24 Apr 1994 Added scatter, gather, gs_get_datasize routines. Donato * */ #ifdef HASSTDLIB #include #endif #include #include #include "pvmalloc.h" #include "bfunc.h" #include "lpvm.h" #include #include "tevmac.h" #include "global.h" #include "pvmmimd.h" #include "pvmgsd.h" #include "pvmproto.h" #ifdef SYSVSTR #include #else #include #endif extern int pvm_errno; extern int pvmmytid; extern int pvmtoplvl; extern struct Pvmtracer pvmtrc; /* ========== ================ * ========== Declarations & Routines for the Paragon ================ * ========== ================ * Note: int and long are the same on the paragon */ #if defined(IMA_PGON) extern int pvmpgonpartsize; /* from lpvmmimd.c */ /* ================ NativeFunction() for the PGON ==================== */ /* This routine compares the address of the user provided function to the built-in pvm functions to determine and then call the appropriate native mode function on the Paragon. */ int NativeFunction(user_func, datatype, x, y, num) #ifdef IMA_SCO void (*user_func)(int*, void*, void*, int*, int*); #else void (*user_func)(); #endif void *x, *y; int datatype, num; { if (user_func==PvmSum) { switch(datatype) { case (PVM_INT): case (PVM_LONG): _gisum((int *) x, num, (int *) y); break; case (PVM_FLOAT): _gssum((float *) x, num, (float *) y); break; case (PVM_DOUBLE): _gdsum((double *) x, num, (double *) y); break; default: return(PvmNotImpl); } /* end switch */ } else if (user_func==PvmMax) { switch(datatype) { case (PVM_INT): case (PVM_LONG): _gihigh((int *) x, num, (int *) y); break; case (PVM_FLOAT): _gshigh((float *) x, num, (float *) y); break; case (PVM_DOUBLE): _gdhigh((double *) x, num, (double *) y); break; default: return(PvmNotImpl); } /* end switch */ } else if (user_func==PvmMin) { switch(datatype) { case (PVM_INT): case (PVM_LONG): _gilow((int *) x, num, (int *) y); break; case (PVM_FLOAT): _gslow((float *) x, num, (float *) y); break; case (PVM_DOUBLE): _gdlow((double *) x, num, (double *) y); break; default: return(PvmNotImpl); } /* end switch */ } else if (user_func==PvmProduct) { switch(datatype) { case (PVM_INT): case (PVM_LONG): _giprod((int *) x, num, (int *) y); break; case (PVM_FLOAT): _gsprod((float *) x, num, (float *) y); break; case (PVM_DOUBLE): _gdprod((double *) x, num, (double *) y); break; default: return(PvmNotImpl); } /* end switch */ } else return(PvmNotImpl); return(PvmOk); } /* end NativeFunction() */ #endif /* #ifdef PGON */ /* ================ pvm_reduce() ===================================== */ /* int info = pvm_reduce(void (*func)(), void *data, int count, int datatype, int msgtag, char *gname, int rootinst) where void (*func)(int *datatype, void *data, void *work, int *num, int *info) Currently, this implementation uses a fan in algorithm to perform the reduce operation. Each group member sends their data to the coordinator on their host machine. The coordinator performs the specified function combining its own data and the data from the group members on the same host. On the Paragon the nx native global operations are utilized if all the nodes of the Paragon are part of the group. The coordinators then pass their results on to the specified root node of the reduce operation. */ int pvm_reduce(func, data, count, datatype, msgtag, gname, rootinst) #ifdef IMA_SCO void (*func)(int*, void*, void*, int*, int*); #else void (*func)(); #endif void *data; int count, datatype, msgtag, rootinst; char *gname; { int cnt, roottid, datasize, cc=PvmOk, rbuf, sbuf; int coordinator, nmembers_on_host, nhosts_in_group, mask=0; void *work = NULL; /* work array to be allocated */ int (*packfunc)(), (*unpackfunc)(); int x; int savectx; TEV_DECLS BGN_TRACE( TEV_REDUCE, gname, TEV_DID_MC, &msgtag ); rbuf = pvm_setrbuf(0); /* set receive buf */ sbuf = pvm_mkbuf(PvmDataDefault); sbuf = pvm_setsbuf(sbuf); /* set context for dynamic groups */ savectx = pvm_setcontext( SYSCTX_DG ); if ( (data == NULL) || (count <= 0) ) /* check some parameters */ { cc = PvmBadParam; goto done; } /* get instance number - caller must be in group, root must be in group */ if ( (cc = pvm_getinst(gname, pvmmytid)) < PvmOk ) goto done; if ( (cc = roottid = pvm_gettid (gname, rootinst)) < PvmOk ) goto done; if ( (cc = datasize = gs_get_datasize(datatype)) < PvmOk ) goto done; /* set up pointers to the appropriate pack and unpack routines */ if ( (cc = gs_pack_unpack(datatype, &packfunc, &unpackfunc) ) < PvmOk) goto done; if ((work = (void *) PVM_ALLOC(count*datasize, "pvm_reduce")) == NULL) { cc = PvmNoMem; goto done; } pvm_grphostinfo(gname, gs_tidtohost(pvmmytid), &coordinator, &nmembers_on_host, &nhosts_in_group); #if defined(IMA_PGON) /* if all the nodes are participating, then call the Native mode version, if one exists. The native function call is a side-effect of the call to NativeFunction. If the coordinator and roottid are PGON nodes, there is no need to differentiate (e.g. pass data from coordinator to roottid). */ mask = TIDHOST | TIDPTYPE; if ( TIDISNODE(pvmmytid) && ( pvmpgonpartsize == nmembers_on_host ) && ( (cc = NativeFunction(func, datatype, data, work, count)) == PvmOk) ) { if ( TIDISNODE(roottid) && (pvmmytid & mask) == (roottid & mask) ) coordinator = roottid; } else #endif /* end if defined(IMA_PGON) */ { if ( (pvmmytid==coordinator) && (nmembers_on_host>1) ) { /* recv data from other group members on same host, perform func */ for (cnt = nmembers_on_host-1; cnt>0; cnt--) { if ( (cc = pvm_recv(-1, msgtag) ) < PvmOk) goto done; if ( (cc = (*unpackfunc)( work, count, 1)) < PvmOk) goto done; (*func)( &datatype, data, work, &count, &cc ); if (cc < PvmOk) goto done; /* error flag from func */ } } else if (pvmmytid != coordinator) { /* send data to the data coordinator on this same host */ pvm_initsend(PvmDataDefault); if ( (cc = (*packfunc)( data, count, 1) ) < PvmOk ) goto done; if ( (cc = pvm_send( coordinator, msgtag)) < PvmOk ) goto done;; } } if ( (pvmmytid==coordinator) && (pvmmytid != roottid) ) { /* send data to the roottid for the reduce operation */ pvm_initsend(PvmDataDefault); if ( (cc = (*packfunc)( data, count, 1) ) < PvmOk ) goto done; if ( (cc = pvm_send( roottid, msgtag)) < PvmOk ) goto done; } /* Root node of the reduce operation: - get data from my host coordinator, if necessary - get work values from each of the other hosts - perform the specified functions on data from other hosts */ if (pvmmytid == roottid) { /* if root isn't the host coordinator, receive from coordinator 1st */ if (pvmmytid != coordinator) { if ( (cc = pvm_recv(coordinator, msgtag) ) < PvmOk) goto done; if ( (cc = (*unpackfunc)( data, count, 1)) < PvmOk) goto done; } if (nhosts_in_group-- <= 0) goto done; /* recv data from other group members on diff host, perform func */ for (cnt = nhosts_in_group; cnt>0; cnt--) { if ( (cc = pvm_recv(-1, msgtag) ) < PvmOk) goto done; if ( (cc = (*unpackfunc)( work, count, 1)) < PvmOk) goto done; (*func)( &datatype, data, work, &count, &cc ); if (cc < PvmOk) goto done; /* error flag from func */ } /* end for */ } cc = PvmOk; done: /* restore user's buffers */ pvm_freebuf(pvm_setrbuf(rbuf)); pvm_freebuf(pvm_setsbuf(sbuf)); /* restore user context */ pvm_setcontext( savectx ); if (work != NULL) PVM_FREE(work); /* free work space */ if (cc < 0) lpvmerr("pvm_reduce",cc); END_TRACE( TEV_REDUCE, TEV_DID_CC, &cc ); return(cc); } /* end pvm_reduce() */ /* ================ gs_pack_unpack()================================== */ /* int info = gs_pack_unpack( int datatype, int (**packfunc)(), int (**unpackfunc)() ) Sets up pointers to the appropriate pack and unpack function based on datatype specified. */ int gs_pack_unpack(datatype, packfunc, unpackfunc) int datatype, (**packfunc)(), (**unpackfunc)(); { switch(datatype) { case (PVM_STR): *packfunc = pvm_pkstr; *unpackfunc = pvm_upkstr; break; case (PVM_BYTE): *packfunc = pvm_pkbyte; *unpackfunc = pvm_upkbyte; break; case (PVM_SHORT): *packfunc = pvm_pkshort; *unpackfunc = pvm_upkshort; break; case (PVM_INT): *packfunc = pvm_pkint; *unpackfunc = pvm_upkint; break; case (PVM_LONG): *packfunc = pvm_pklong; *unpackfunc = pvm_upklong; break; case (PVM_FLOAT): *packfunc = pvm_pkfloat; *unpackfunc = pvm_upkfloat; break; case (PVM_DOUBLE): *packfunc = pvm_pkdouble; *unpackfunc = pvm_upkdouble; break; case (PVM_CPLX): *packfunc = pvm_pkcplx; *unpackfunc = pvm_upkcplx; break; case (PVM_DCPLX): *packfunc = pvm_pkdcplx; *unpackfunc = pvm_upkdcplx; break; default: return(PvmBadParam); } return(PvmOk); } /* ================ PvmMax()========================================== */ /* void PvmMax(int *datatype, void *x, void *y, int *num, int *info) Assigns the elements of x the maximum value between the corresponding elements of x and y. For complex values the maximum is determined by maximum modulus. */ void PvmMax(datatype, x, y, num, info) int *datatype; void *x, *y; int *num, *info; { char *xchar, *ychar; int *xint, *yint; short *xshort, *yshort; long *xlong, *ylong; float *xfloat, *yfloat; double *xdouble, *ydouble; float xfreal, xfimag, yfreal, yfimag; double xdreal, xdimag, ydreal, ydimag; float xsqrfloat, ysqrfloat; double xsqrdouble, ysqrdouble; int i, count; count = *num; switch(*datatype) { case (PVM_BYTE): xchar = (char *) x; ychar = (char *) y; for (i=0; i xsqrfloat) { xfloat[i] = yfreal; xfloat[i+1] = yfimag; } } break; case (PVM_DCPLX): /* double complex - complex*16 in fortran - treated as 2 doubles */ /* returns the complex pair with the greatest magnitude */ xdouble = (double *) x; ydouble = (double *) y; for (i=0; i<2*count; i+=2) { xdreal = xdouble[i]; xdimag = xdouble[i+1]; ydreal = ydouble[i]; ydimag = ydouble[i+1]; xsqrdouble = xdreal*xdreal + xdimag*xdimag; ysqrdouble = ydreal*ydreal + ydimag*ydimag; if (ysqrdouble > xsqrdouble) { xdouble[i] = ydreal; xdouble[i+1] = ydimag; } } break; default: *info = PvmBadParam; return; } /* end switch */ *info = PvmOk; return; } /* end of PvmMax() */ /* ================ PvmMin()========================================== */ /* void PvmMin(int *datatype, void *x, void *y, int *num, int *info) Assigns the elements of x the minimum value between the corresponding elements of x and y. For complex values the minimum is determined by minimum modulus. */ void PvmMin(datatype, x, y, num, info) int *datatype; void *x, *y; int *num, *info; { char *xchar, *ychar; short *xshort, *yshort; int *xint, *yint; long *xlong, *ylong; float *xfloat, *yfloat; double *xdouble, *ydouble; float xfreal, xfimag, yfreal, yfimag; double xdreal, xdimag, ydreal, ydimag; float xsqrfloat, ysqrfloat; double xsqrdouble, ysqrdouble; int i, count; count = *num; switch(*datatype) { case (PVM_BYTE): xchar = (char *) x; ychar = (char *) y; for (i=0; i