![]() |
Gowdy solver
|
00001 /** 00002 * @file mpiWorld.h 00003 * @brief The MPI implementation that splits a grid among the ranks. 00004 * @author Mikica Kocic 00005 * @copyright GNU General Public License (GPLv3). 00006 */ 00007 00008 #include <mpi.h> 00009 00010 #ifdef _STANDALONEMPI 00011 #include <cstdio> 00012 #include <cstdlib> 00013 #include <cmath> 00014 #define MPIWorld_sanityCheck main 00015 #endif 00016 00017 /** Implements the message passing interface (MPI) for high-performance computing (HPC). 00018 * 00019 * In the MPI environment, the calculations on the grid are split among the ranks 00020 * so the edges are exchanged between each two neighbors in the following way: 00021 * <pre> 00022 * .---- left -----.-------.---- right ----. 00023 * | ghost | edge | | edge | ghost | 00024 * +-------+-----------------------+-------+ 00025 * ... | | grid chunk i | | 00026 * +-------+-----------------------+-------+------------+-------+ 00027 * | | grid chunk i+1 | | ... 00028 * +-------+--------------------+-------+ 00029 * : left : 00030 * : ghost | edge : 00031 * `-------v-------ยด 00032 * exchanged between two neighbors </pre> 00033 * 00034 * The overlapping edges of one rank become other's ghosts (of the same size). 00035 * The ghost of the left-most rank is determined from the inner boundary conditions and 00036 * the ghost of the right-most rank is determined from the outer boundary conditions. 00037 * 00038 * When compiling, use `mpic++` with `-D_USEMPI`. To run, use `mpiexec`. 00039 * 00040 * @warning The maximal slicing is not compliant with MPI since the boundary value 00041 * problem for the slicing differential equation requires access to the whole grid! 00042 */ 00043 class MPIWorld 00044 { 00045 int worldSize; //!< The size of the MPI world 00046 int myRank; //!< Our rank in the MPI world 00047 int leftRank; //!< The rank of the left neighbor 00048 int rightRank; //!< The rank of the right neighbor 00049 MPI_Request waitLeft; //!< Outstanding MPI_Isend request to the left rank 00050 MPI_Request waitRight; //!< Outstanding MPI_Isend request to the right rank 00051 00052 public: 00053 00054 /** Initializes the MPI environment and finds our rank in the MPI world. 00055 */ 00056 MPIWorld () 00057 { 00058 // Initialize the MPI environment 00059 // 00060 MPI_Init( NULL, NULL ); 00061 00062 // Find out the rank and the size of the MPI 00063 // 00064 MPI_Comm_rank( MPI_COMM_WORLD, &myRank ); 00065 MPI_Comm_size( MPI_COMM_WORLD, &worldSize ); 00066 00067 leftRank = isFirstInRank() ? -1 : myRank - 1; 00068 rightRank = isLastInRank() ? -1 : myRank + 1; 00069 00070 waitLeft = waitRight = MPI_REQUEST_NULL; 00071 00072 // std::printf( "my rank %d, left %d, right %d\n", myRank, leftRank, rightRank ); 00073 } 00074 00075 ~MPIWorld () 00076 { 00077 MPI_Finalize (); 00078 } 00079 00080 int size () const { 00081 return worldSize; 00082 } 00083 00084 int rank () const { 00085 return myRank; 00086 } 00087 00088 bool isFirstInRank () const { 00089 return myRank == 0; 00090 } 00091 00092 bool isLastInRank () const { 00093 return myRank == worldSize - 1; 00094 } 00095 00096 /** Send our edges (they become other's ghosts) and 00097 * receive other's edges (they become our ghosts). 00098 */ 00099 void exchangeBoundaries 00100 ( 00101 void* left_ghost, //!< Where to put the right edge of the left neighbor 00102 void* left_edge, //!< Pointer to the left edge of our data 00103 void* right_edge, //!< Pointer to the right edge of our data 00104 void* right_ghost, //!< Where to put the left edge of the right neighbor 00105 int edge_size //!< The edge (or ghost) size 00106 ) 00107 { 00108 waitLeft = waitRight = MPI_REQUEST_NULL; 00109 00110 MPI_Datatype data_type = sizeof(Real) == sizeof(double) 00111 ? MPI_DOUBLE : MPI_LONG_DOUBLE; 00112 00113 // Send our edges (they become other's ghosts) 00114 00115 if ( leftRank >= 0 ) { 00116 MPI_Isend( left_edge, edge_size, data_type, leftRank, 0, 00117 MPI_COMM_WORLD, &waitLeft ); 00118 } 00119 if ( rightRank >= 0 ) { 00120 MPI_Isend( right_edge, edge_size, data_type, rightRank, 0, 00121 MPI_COMM_WORLD, &waitRight ); 00122 } 00123 00124 // Receive other's edges (they become our ghosts) 00125 // 00126 if ( leftRank >= 0 ) { 00127 MPI_Recv( left_ghost, edge_size, data_type, leftRank, 0, 00128 MPI_COMM_WORLD, MPI_STATUS_IGNORE ); 00129 } 00130 if ( rightRank >= 0 ) { 00131 MPI_Recv( right_ghost, edge_size, data_type, rightRank, 0, 00132 MPI_COMM_WORLD, MPI_STATUS_IGNORE ); 00133 } 00134 } 00135 00136 /** Wait the other ranks to have received our edges. 00137 */ 00138 void waitExchange () 00139 { 00140 if ( waitLeft != NULL ) { 00141 MPI_Status status; 00142 MPI_Wait( &waitLeft, &status ); 00143 } 00144 if ( waitRight != NULL ) { 00145 MPI_Status status; 00146 MPI_Wait( &waitRight, &status ); 00147 } 00148 } 00149 }; 00150 00151 /** A sanity check test-bed with benchmarking. 00152 * To test, replace `main` by MPIWorld_sanityCheck then invoke 00153 * `mpiexec -n 4 bim-solver 10 10 10 1`. 00154 */ 00155 int MPIWorld_sanityCheck( int argc, char** argv ) 00156 { 00157 const Int nGhost = argc >= 2 ? atol ( argv[1] ) : 1; 00158 const Int mLen = argc >= 3 ? atol ( argv[2] ) : 1; 00159 const Int nTotal = argc >= 4 ? atol ( argv[2] ) : 80000; 00160 const bool debug = argc >= 5; 00161 00162 MPIWorld mpi; 00163 00164 Int nLen = nTotal / mpi.size(); // nLen should be a multiple of mpi.size() 00165 Real* gridRow = new Real[ nLen + 2 * nGhost ]; 00166 Real* myData = gridRow + nGhost; 00167 Real* left_ghost = gridRow; 00168 Real* left_edge = gridRow + nGhost; 00169 Real* right_edge = gridRow + nGhost + nLen - nGhost; 00170 Real* right_ghost = gridRow + nGhost + nLen; 00171 00172 for ( Int m = 0; m < mLen; ++m ) 00173 { 00174 left_ghost [0] = -1; 00175 left_edge [0] = ( mpi.rank () + 1 ) * 1000 + m * 100 + 1; 00176 myData [0] = ( mpi.rank () + 1 ) * 1000 + m * 100 + 2; 00177 right_edge [0] = ( mpi.rank () + 1 ) * 1000 + m * 100 + 3; 00178 right_ghost [0] = -1; 00179 00180 if ( debug ) { 00181 std::printf( "PRE %*s %5.0lf|%5.0lf%5.0lf%5.0lf|%5.0lf\n", mpi.rank() * 16, 00182 "", *left_ghost, *left_edge, *myData, *right_edge, *right_ghost ); 00183 } 00184 00185 mpi.exchangeBoundaries( 00186 left_ghost, left_edge, right_edge, right_ghost, nGhost ); 00187 00188 // Do a sample calculation (equally divided among the ranks) 00189 // The calculation depends on the left ghost. 00190 for ( Int n = nGhost; n < nGhost + nLen; ++n ) { 00191 gridRow[n] = gridRow[n-1] + cos(n) * exp( n * sin(n) ); 00192 } 00193 00194 mpi.waitExchange (); 00195 00196 if ( debug ) { 00197 printf( "POST%*s %5.0lf|%5.0lf%5.0lf%5.0lf|%5.0lf\n", mpi.rank() * 16, 00198 "", *left_ghost, *left_edge, *myData, *right_edge, *right_ghost ); 00199 } 00200 } 00201 00202 return 0; 00203 }