00001 #ifndef MPI_STATS_H 00002 #define MPI_STATS_H 00003 00004 //-------------------------------------------------------------------- 00005 // 00006 // This file is part of PEACE. 00007 // 00008 // PEACE is free software: you can redistribute it and/or modify it 00009 // under the terms of the GNU General Public License as published by 00010 // the Free Software Foundation, either version 3 of the License, or 00011 // (at your option) any later version. 00012 // 00013 // PEACE is distributed in the hope that it will be useful, but 00014 // WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU General Public License 00019 // along with PEACE. If not, see <http://www.gnu.org/licenses/>. 00020 // 00021 // Miami University makes no representations or warranties about the 00022 // suitability of the software, either express or implied, including 00023 // but not limited to the implied warranties of merchantability, 00024 // fitness for a particular purpose, or non-infringement. Miami 00025 // University shall not be liable for any damages suffered by licensee 00026 // as a result of using, result of using, modifying or distributing 00027 // this software or its derivatives. 00028 // 00029 // By using or copying this Software, Licensee agrees to abide by the 00030 // intellectual property laws, and all other applicable laws of the 00031 // U.S., and the terms of GNU General Public License (version 3). 00032 // 00033 // Authors: Dhananjai M. Rao raodm@muohio.edu 00034 // 00035 //--------------------------------------------------------------------- 00036 00037 #include <iostream> 00038 #include <string> 00039 00040 // Include our non-native MPI header to enable/diable MPI usage. 00041 #include "MPIHelper.h" 00042 00043 /** A non-instantiable wrapper class to track MPI-related statistics. 00044 00045 This class is a simple wrapper class that is used to encapsulate 00046 the statistic gathered during program execution. The statistics 00047 collated by this class related to MPI calls. In order to 00048 facilitate statistics collection without cluttering MPI code, this 00049 class provides a set of handy macros for various MPI macro calls, 00050 namely: MPI_PROBE, MPI_SEND, MPI_RECV, MPI_BCAST, and 00051 TRACK_IDLE_TIME. Use the aforementioned macros to streamline your 00052 code. Finally, at the end of MPI processing use 00053 MPIStats::displayStats() method do display the statistics gathered 00054 by this method. 00055 */ 00056 class MPIStats { 00057 public: 00058 /** Cumulative time spent idling. 00059 00060 This \b static instance variable (initialized to 0) maintains 00061 the cumulative time spent by this process idling, waiting for 00062 messages to arrive from another process. This value is 00063 tracked by wrapping each MPI probe call (or MPI recv as the 00064 case may be) with the TRACK_IDLE_TIME macro. The time is 00065 tracked using the Wtime() method defined by MPI. 00066 */ 00067 static double idleTime; 00068 00069 /** Number of messages sent by this process. 00070 00071 <p> This \b static instance variable (initialized to 0 (zero)) 00072 is used to essentially track the number of calls to MPI send 00073 method. This variable must be incremented each time MPI's 00074 send method is invoked. This variable is finally printed in 00075 the displayStats() method.<p> 00076 00077 \note Using the MPI_SEND macro call streamlines this process 00078 as it increments the send count as well. 00079 */ 00080 static int sendCount; 00081 00082 /** Number of messages received by this process. 00083 00084 <p>This \b static instance variable (initialized to 0 (zero)) 00085 is used to essentially track the number of calls to MPI recv 00086 method. This variable must be incremented each time MPI's 00087 recv method is invoked. This variable is finally printed in 00088 the displayStats() method.</p> 00089 00090 <p>If you would also like to track the time spent to recv a 00091 certain message as idle time then you can easily achieve this 00092 objective as shown in the code fragement below:</p> 00093 00094 \code 00095 #include "MPIHelper.h" 00096 00097 void someMethod() { 00098 TRACK_IDLE_TIME(MPI_SEND(b, 1, MPI_TYPE_INT, rank, tag)); 00099 } 00100 \endcode 00101 00102 \note Using the MPI_RECV macro call streamlines this process 00103 as it increments the recvCount as well. 00104 */ 00105 static int recvCount; 00106 00107 /** Number of messages broad casted by this process. 00108 00109 This \b static instance variable (initialized to 0 (zero)) is 00110 used to essentially track the number of calls to MPI bcast 00111 method. This variable must be incremented each time MPI's 00112 bcast method is invoked. This variable is finally printed in 00113 the displayStats() method. 00114 00115 \note Using the MPI_BCAST macro call streamlines this process 00116 as it increments the bcastCount as well. 00117 */ 00118 static int bcastCount; 00119 00120 /** Number of times reduction operation was performed by this process. 00121 00122 This \b static instance variable (initialized to 0 (zero)) is 00123 used to essentially track the number of calls to MPI reduce 00124 method. This variable must be incremented each time MPI's 00125 reduce method is invoked. This variable is finally printed in 00126 the displayStats() method. 00127 00128 \note Using the MPI_REDUCE macro call streamlines this process 00129 as it increments the reduceCount as well. 00130 */ 00131 static int reduceCount; 00132 00133 /** Number of time this process probed for messages. 00134 00135 This \b static instance variable (initialized to 0 (zero)) is 00136 used to essentially track the number of calls to MPI probe 00137 method. This variable must be incremented each time MPI's 00138 bcast method is invoked. This variable is finally printed in 00139 the displayStats() method. 00140 00141 \note Using the MPI_PROBE macro call streamlines this process 00142 as it increments the probeCount as well. 00143 */ 00144 static int probeCount; 00145 00146 /** Displays statistics collated thus far. 00147 00148 This method is invoked at the end of the makeClusters() method 00149 to display performance related statistics for this class. 00150 This method displays the following information: 00151 00152 <ul> 00153 00154 <li> The time spent idling waiting for messages to arrive (in 00155 seconds). This information is tracked in the idleTime 00156 instance variable. </li> 00157 00158 <li> The number of messages that were sent (essentially calls 00159 to MPI Send method). This information is tracked in the 00160 sendCount instance variable. </li> 00161 00162 <li> The number of messages that were received (essentially 00163 calls to MPI Recv method). This information is tracked in the 00164 recvCount instance variable. </li> 00165 00166 <li> The number of calls to broadcast (essentially calls to 00167 MPI Bcast method). This information is tracked in the 00168 bcastCount instance variable. </li> 00169 00170 <li> The number of reduction operations (essentially calls to 00171 MPI Reduce method). This information is tracked in the 00172 reduceCount instance variable. </li> 00173 00174 <li> The number of calls to proble (essentially calls to MPI 00175 probe method). This information is tracked in the probeCount 00176 instance variable. </li> 00177 00178 </ul> 00179 00180 \param[out] os The output stream to which all the information 00181 must be written. 00182 00183 \param[in] indent An indentation prefix to be used to indent 00184 the outputs generated by this method. The default indentation 00185 prefix is 4 spaces. 00186 */ 00187 static void displayStats(std::ostream& os, 00188 const std::string& indent = " "); 00189 protected: 00190 // Currently this class has no protected members. 00191 00192 private: 00193 /** The default constructor. 00194 00195 The default constructor has is private in order to ensure that 00196 this class is never instantiated. Instead, the static methods 00197 and static instance variables in this class are directly used. 00198 */ 00199 MPIStats() {} 00200 00201 /** The destructor. 00202 00203 The destructor is private to ensure that objects of this class 00204 can never be deleted (as they cannot be created either). 00205 */ 00206 ~MPIStats() {} 00207 }; 00208 00209 /** \def TRACK_IDLE_TIME 00210 00211 \brief Convenience macro to track time spent in calling a MPI 00212 method (only if MPI is available). 00213 00214 This macro provides a convenient wrapper around a given MPI method 00215 call to track the time spent in calling a MPI method. This macro 00216 assumes that the MPI call being made must be accounted as idle 00217 time for the process and uses the Wtime() method to appropriately 00218 time the method call and adds the elapsed time to 00219 MPIStats::idleTime variable. This macro must be used as shown 00220 below: 00221 00222 \code 00223 00224 #include "MPIHelper.h" 00225 00226 void someMethod() { 00227 MPI::Status statusInfo; 00228 TRACK_IDLE_TIME(MPI::COMM_WORLD.Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, 00229 statusInfo)); 00230 } 00231 00232 \endcode 00233 00234 \note If you are tracking idle time for the Probe method, use 00235 MPI_PROBE macro directly as it automatically tracks the idle time 00236 too. 00237 */ 00238 #ifdef HAVE_LIBMPI 00239 #define TRACK_IDLE_TIME(MPIMethodCall) { \ 00240 const double startTime = MPI::Wtime(); \ 00241 MPIMethodCall; \ 00242 MPIStats::idleTime += (MPI::Wtime() - startTime); \ 00243 } 00244 #else 00245 // If we don't have MPI this TRACK_IDLE_TIME reduces to nothing. 00246 #define TRACK_IDLE_TIME(MPIMethodCall) 00247 #endif 00248 00249 /** \def MPI_PROBE 00250 00251 \brief Convenience macro to track time spent in calling a 00252 MPI::COMM_WORLD.Probe() method only if MPI is enabled. If MPI is 00253 not enabled, then this macro does nothing and call to MPI_Probe is 00254 never made. 00255 00256 This macro provides a convenient wrapper around MPI's Probe method 00257 call to update MPIStats::probeCount variable and update the 00258 MPIStats::idleTime variable with the spent in the blocking Probe 00259 method. This macro assumes that the Probe call being made must be 00260 accounted as idle time for the process and uses the Wtime() method 00261 to appropriately time the method call and adds the elapsed time to 00262 MPIStats::idleTime variable. This macro must be used as shown 00263 below: 00264 00265 \code 00266 00267 #include "MPIHelper.h" 00268 00269 void someMethod() { 00270 // ... some code goes here .. 00271 MPI::Status statusInfo; 00272 MPI_PROBE(MPI_ANY_SOURCE, MPI_ANY_TAG, statusInfo)); 00273 // ... more code goes here .. 00274 } 00275 00276 \endcode 00277 00278 \note This macro call checks the statsInfo after the Probe call to 00279 ensure that the MPI call completed successfully. If errors are 00280 found, then this method throws an exception. 00281 */ 00282 #ifdef HAVE_LIBMPI 00283 #define MPI_PROBE(rank, tag, statusInfo) { \ 00284 const double startTime = MPI::Wtime(); \ 00285 MPI::COMM_WORLD.Probe(rank, tag, statusInfo); \ 00286 if ((statusInfo.Get_error() != MPI::SUCCESS) || \ 00287 (statusInfo.Is_cancelled())) { \ 00288 throw MPI::Exception(statusInfo.Get_error()); \ 00289 } \ 00290 MPIStats::idleTime += (MPI::Wtime() - startTime); \ 00291 MPIStats::probeCount++; \ 00292 } 00293 #else 00294 // If we don't have MPI this MPI_PROBE reduces to nothing. 00295 #define MPI_PROBE(rank, tag, statusInfo) 00296 #endif 00297 00298 /** \def MPI_SEND 00299 00300 \brief Convenience macro to track number of calls to 00301 MPI::COMM_WORLD.Send() method. 00302 00303 This macro provides a convenient wrapper around MPI's Send method 00304 call to update MPIStats::sendCount variable. This macro can be 00305 used as shown below: 00306 00307 \code 00308 00309 #include "MPIHelper.h" 00310 00311 void someMethod() { 00312 // ... some code goes here .. 00313 MPI_SEND(data, count, mpiDataType, rank, tag); 00314 // ... more code goes here .. 00315 } 00316 00317 \endcode 00318 00319 \note This macro calls MPI::COMM_WORD.Send only if MPI is 00320 enabled. If MPI is not enabled (or is unavailable) then this macro 00321 reduces to nothing and the actual send call is never made. 00322 */ 00323 #ifdef HAVE_LIBMPI 00324 #define MPI_SEND(data, count, dataType, rank, tag) { \ 00325 MPI::COMM_WORLD.Send(data, count, dataType, rank, tag); \ 00326 MPIStats::sendCount++; \ 00327 } 00328 #else 00329 // If we don't have MPI then MPI_SEND reduces to nothing. 00330 #define MPI_SEND(data, count, dataType, rank, tag) 00331 #endif 00332 00333 /** \def MPI_RECV 00334 00335 \brief Convenience macro to track number of calls to 00336 MPI::COMM_WORLD.Recv() method. 00337 00338 This macro provides a convenient wrapper around MPI's Recv method 00339 call to update MPIStats::recvCount variable. This macro can be 00340 used as shown below: 00341 00342 \code 00343 00344 #include "MPIHelper.h" 00345 00346 void someMethod() { 00347 // ... some code goes here .. 00348 MPI_RECV(data, count, mpiDataType, rank, tag); 00349 // ... more code goes here .. 00350 } 00351 00352 \endcode 00353 00354 \note This macro calls MPI::COMM_WORD.Send only if MPI is 00355 enabled. If MPI is not enabled (or is unavailable) then this macro 00356 reduces to nothing and the actual receive call is never made. 00357 */ 00358 #ifdef HAVE_LIBMPI 00359 #define MPI_RECV(data, count, dataType, rank, tag) { \ 00360 MPI::COMM_WORLD.Recv(data, count, dataType, rank, tag); \ 00361 MPIStats::recvCount++; \ 00362 } 00363 #else 00364 #define MPI_RECV(data, count, dataType, rank, tag) 00365 #endif 00366 00367 /** \def MPI_BCAST 00368 00369 \brief A simple, convenience macro to track number of calls to 00370 MPI::COMM_WORLD.Bcast() method. 00371 00372 This macro provides a convenient wrapper around MPI's Bcast method 00373 call to update MPIStats::bcastCount variable. This macro can be 00374 used as shown below: 00375 00376 \code 00377 00378 #include "MPIHelper.h" 00379 00380 void someMethod() { 00381 // ... some code goes here .. 00382 MPI_BCAST(data, count, mpiDataType, senderRank); 00383 // ... more code goes here .. 00384 } 00385 00386 \endcode 00387 00388 \note This macro calls MPI::COMM_WORD.Bcast only if MPI is 00389 enabled. If MPI is not enabled (or is unavailable) then this macro 00390 reduces to nothing and the actual broadcast call is never made. 00391 */ 00392 #ifdef HAVE_LIBMPI 00393 #define MPI_BCAST(data, count, dataType, senderRank) { \ 00394 MPI::COMM_WORLD.Bcast(data, count, dataType, senderRank); \ 00395 MPIStats::bcastCount++; \ 00396 } 00397 #else 00398 // If we don't have MPI then MPI_BCAST reduces to nothing. 00399 #define MPI_BCAST(data, count, dataType, senderRank) 00400 #endif 00401 00402 /** \def MPI_REDUCE 00403 00404 \brief A simple, convenience macro to track number of calls to 00405 MPI::COMM_WORLD.Reduce() method. 00406 00407 This macro provides a convenient wrapper around MPI's Reduce 00408 method call to update MPIStats::reduceCount variable. This macro 00409 can be used as shown below: 00410 00411 \code 00412 00413 #include "MPIHelper.h" 00414 00415 void someMethod() { 00416 // ... some code goes here .. 00417 int localSuccess = 10, totalSuccess = 0; 00418 MPI_REDUCE(&localSuccess, &totalSuccess, 1, MPI::INT, MPI::SUM, 0); 00419 // ... more code goes here .. 00420 } 00421 00422 \endcode 00423 00424 \note This macro calls MPI::COMM_WORD.Reduce only if MPI is 00425 enabled. If MPI is not enabled (or is unavailable) then this macro 00426 reduces to nothing and the actual broadcast call is never made. 00427 */ 00428 #ifdef HAVE_LIBMPI 00429 #define MPI_REDUCE(srcBufr, destBufr, count, dataType, op, destRank) { \ 00430 MPI::COMM_WORLD.Reduce(srcBufr, destBufr, count, dataType, \ 00431 op, destRank); \ 00432 MPIStats::reduceCount++; \ 00433 } 00434 #else 00435 // If we don't have MPI then MPI_BCAST reduces to nothing. 00436 #define MPI_REDUCE(srcBufr, destBufr, count, dataType, op, destRank) 00437 #endif 00438 00439 #endif