Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Core.cpp
1 // @HEADER
2 // *****************************************************************************
3 // Tpetra: Templated Linear Algebra Services Package
4 //
5 // Copyright 2008 NTESS and the Tpetra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #include "Tpetra_Core.hpp"
11 #include "Tpetra_Details_mpiIsInitialized.hpp"
12 
13 #ifdef HAVE_TPETRACORE_MPI
14 # include <Teuchos_DefaultMpiComm.hpp> // this includes mpi.h too
15 #endif // HAVE_TPETRACORE_MPI
16 #include <Teuchos_DefaultSerialComm.hpp>
17 
18 #include <Kokkos_Core.hpp>
19 #include "Tpetra_Details_checkLaunchBlocking.hpp"
22 
23 namespace Tpetra {
24 
25  namespace { // (anonymous)
26 
27  class HideOutputExceptOnProcess0 {
28  public:
29  HideOutputExceptOnProcess0 (std::ostream& stream,
30  const int myRank) :
31  stream_ (stream),
32  originalBuffer_ (stream.rdbuf ())
33  {
34  if (myRank != 0) {
35  stream.rdbuf (blackHole_.rdbuf ());
36  }
37  }
38 
39  ~HideOutputExceptOnProcess0 () {
40  stream_.rdbuf (originalBuffer_);
41  }
42  private:
43  std::ostream& stream_;
44  decltype (std::cout.rdbuf ()) originalBuffer_;
45  Teuchos::oblackholestream blackHole_;
46  };
47 
48 #if defined(HAVE_TPETRACORE_MPI)
49  bool mpiIsInitializedAndNotFinalized ()
50  {
51  int isInitialized = 0;
52  int isFinalized = 0;
53  // Not sure if MPI_Initialized or MPI_Finalized meet the strong
54  // exception guarantee.
55  try {
56  (void) MPI_Initialized (&isInitialized);
57  }
58  catch (...) {
59  isInitialized = 0;
60  }
61  try {
62  (void) MPI_Finalized (&isFinalized);
63  }
64  catch (...) {
65  isFinalized = 0;
66  }
67  return isInitialized != 0 && isFinalized == 0;
68  }
69 
70  int getRankHarmlessly (MPI_Comm comm)
71  {
72  int myRank = 0;
73  if (mpiIsInitializedAndNotFinalized ()) {
74  try {
75  (void) MPI_Comm_rank (comm, &myRank);
76  }
77  catch (...) {
78  // Not sure if MPI_Comm_rank meets strong exception guarantee
79  myRank = 0;
80  }
81  }
82  return myRank;
83  }
84 #endif // defined(HAVE_TPETRACORE_MPI)
85 
86 
87  // Whether one of the Tpetra::initialize() functions has been called before.
88  bool tpetraIsInitialized_ = false;
89 
90  // Whether Tpetra initialized Kokkos. Tpetra::finalize only
91  // finalizes Kokkos if it initialized Kokkos. Otherwise,
92  // something else initialized Kokkos and is responsible for
93  // finalizing it.
94  bool tpetraInitializedKokkos_ = false;
95 
96 #ifdef HAVE_TPETRACORE_MPI
97  // Whether Tpetra initialized MPI. Tpetra::finalize only
98  // finalizes MPI if it initialized MPI. Otherwise, something else
99  // initialized MPI and is responsible for finalizing it.
100  bool tpetraInitializedMpi_ = false;
101 #endif // HAVE_TPETRACORE_MPI
102 
103  // Tpetra's default communicator, wrapped in a Teuchos wrapper.
104  // After Tpetra::finalize() is called, this GOES AWAY (is set to null).
105  Teuchos::RCP<const Teuchos::Comm<int> > wrappedDefaultComm_;
106 
107  // This takes the same arguments as (the first two of) initialize().
108  void initKokkosIfNeeded (int* argc, char*** argv, const int myRank)
109  {
110  if (! tpetraInitializedKokkos_) {
111  // Kokkos doesn't have a global is_initialized(). However,
112  // Kokkos::initialize() always initializes the default execution
113  // space, so it suffices to check whether that was initialized.
114  const bool kokkosIsInitialized =
115  Kokkos::is_initialized ();
116  if (! kokkosIsInitialized) {
117  HideOutputExceptOnProcess0 hideCerr (std::cerr, myRank);
118  HideOutputExceptOnProcess0 hideCout (std::cout, myRank);
119 
120  // Unlike MPI_Init, Kokkos promises not to modify argc and argv.
121  Kokkos::initialize (*argc, *argv);
122  tpetraInitializedKokkos_ = true;
123  }
124  }
125  Details::checkOldCudaLaunchBlocking();
126  const bool kokkosIsInitialized =
127  Kokkos::is_initialized ();
128  TEUCHOS_TEST_FOR_EXCEPTION
129  (! kokkosIsInitialized, std::logic_error, "At the end of "
130  "initKokkosIfNeeded, Kokkos is not initialized. "
131  "Please report this bug to the Tpetra developers.");
132  }
133 
134 #ifdef HAVE_TPETRACORE_MPI
135  // This takes the same arguments as MPI_Init and the first two
136  // arguments of initialize().
137  void initMpiIfNeeded (int* argc, char*** argv)
138  {
139  // Both MPI_Initialized and MPI_Finalized report true after
140  // MPI_Finalize has been called. It's not legal to call
141  // MPI_Init after MPI_Finalize has been called (see MPI 3.0
142  // Standard, Section 8.7). It would be unusual for users to
143  // want to use Tpetra after MPI_Finalize has been called, but
144  // there's no reason why we should forbid it. It just means
145  // that Tpetra will need to run without MPI.
146 
147  const bool mpiReady = mpiIsInitializedAndNotFinalized ();
148  if (! mpiReady) {
149  // Tpetra doesn't currently need to call MPI_Init_thread,
150  // since with Tpetra, only one thread ever calls MPI
151  // functions. If we ever want to explore
152  // MPI_THREAD_MULTIPLE, here would be the place to call
153  // MPI_Init_thread.
154  const int err = MPI_Init (argc, argv);
155  TEUCHOS_TEST_FOR_EXCEPTION
156  (err != MPI_SUCCESS, std::runtime_error, "MPI_Init failed with "
157  "error code " << err << " != MPI_SUCCESS. If MPI was set up "
158  "correctly, then this should not happen, since we have already "
159  "checked that MPI_Init (or MPI_Init_thread) has not yet been "
160  "called. This may indicate that your MPI library is corrupted "
161  "or that it is incorrectly linked to your program.");
162  tpetraInitializedMpi_ = true;
163  }
164  }
165 #endif // HAVE_TPETRACORE_MPI
166 
167  } // namespace (anonymous)
168 
169  bool isInitialized () {
170  return tpetraIsInitialized_;
171  }
172 
173  Teuchos::RCP<const Teuchos::Comm<int> > getDefaultComm ()
174  {
175  // It's technically not correct to call this function if Tpetra
176  // has not yet been initialized, but requiring that may break some
177  // downstream tests.
178  //
179  // This function initializes wrappedDefaultComm_ lazily.
180  // Tpetra::initialize should not set it up.
181  if (wrappedDefaultComm_.is_null ()) {
182  Teuchos::RCP<const Teuchos::Comm<int> > comm;
183 #ifdef HAVE_TPETRACORE_MPI
184  // Teuchos::MpiComm's constructor used to invoke MPI collectives.
185  // It still reserves the right to do so. This means MPI must be
186  // initialized and not finalized.
187  const bool mpiReady = mpiIsInitializedAndNotFinalized ();
188  if (mpiReady) {
189  comm = Teuchos::rcp (new Teuchos::MpiComm<int> (MPI_COMM_WORLD));
190  }
191  else {
192  comm = Teuchos::rcp (new Teuchos::SerialComm<int> ());
193  }
194 #else
195  comm = Teuchos::rcp (new Teuchos::SerialComm<int> ());
196 #endif // HAVE_TPETRACORE_MPI
197  wrappedDefaultComm_ = comm;
198  }
199  return wrappedDefaultComm_;
200  }
201 
202  void initialize (int* argc, char*** argv)
203  {
204  if (! tpetraIsInitialized_) {
205 #if defined(HAVE_TPETRACORE_MPI)
206  initMpiIfNeeded (argc, argv);
207  // It's technically legal to initialize Tpetra after
208  // MPI_Finalize has been called. This means that we can't call
209  // MPI_Comm_rank without first checking MPI_Finalized.
210  const int myRank = getRankHarmlessly (MPI_COMM_WORLD);
211 #else
212  const int myRank = 0;
213 #endif // defined(HAVE_TPETRACORE_MPI)
214  initKokkosIfNeeded (argc, argv, myRank);
215 
216  // Add Kokkos calls to the TimeMonitor if the environment says so
217  Tpetra::Details::AddKokkosDeepCopyToTimeMonitor();
218  Tpetra::Details::AddKokkosFenceToTimeMonitor();
219  Tpetra::Details::AddKokkosFunctionsToTimeMonitor();
220 
222  }
223  tpetraIsInitialized_ = true;
224  }
225 
226 #ifdef HAVE_TPETRACORE_MPI
227  void initialize (int* argc, char*** argv, MPI_Comm comm)
228  {
229  if (! tpetraIsInitialized_) {
230 #if defined(HAVE_TPETRACORE_MPI)
231  initMpiIfNeeded (argc, argv);
232  // It's technically legal to initialize Tpetra after
233  // MPI_Finalize has been called. This means that we can't call
234  // MPI_Comm_rank without first checking MPI_Finalized.
235  const int myRank = getRankHarmlessly (comm);
236 #else
237  const int myRank = 0;
238 #endif // defined(HAVE_TPETRACORE_MPI)
239  initKokkosIfNeeded (argc, argv, myRank);
240 
241  // Add Kokkos::deep calls to the TimeMonitor if the environment says so
242  Tpetra::Details::AddKokkosDeepCopyToTimeMonitor();
243  Tpetra::Details::AddKokkosFenceToTimeMonitor();
244  Tpetra::Details::AddKokkosFunctionsToTimeMonitor();
245 
247  }
248  tpetraIsInitialized_ = true;
249 
250  // Set the default communicator. We set it here, after the above
251  // initialize() call, just in case users have not yet initialized
252  // MPI. (This is legal if users pass in a predefined
253  // communicator, like MPI_COMM_WORLD or MPI_COMM_SELF.)
254  //
255  // What if users have already called initialize() before, but with
256  // a different default communicator? There are two possible
257  // things we could do here:
258  //
259  // 1. Test via MPI_Comm_compare whether comm differs from the
260  // raw MPI communicator in wrappedDefaultComm_ (if indeed it
261  // is an MpiComm).
262  // 2. Accept that the user might want to change the default
263  // communicator, and let them do it.
264  //
265  // I prefer #2. Perhaps it would be sensible to print a warning
266  // here, but on which process? Would we use the old or the new
267  // communicator to find that process' rank? We don't want to use
268  // MPI_COMM_WORLD's Process 0, since neither communicator might
269  // include that process. Furthermore, in some environments, only
270  // Process 0 in MPI_COMM_WORLD is allowed to do I/O. Thus, we
271  // just let the change go without a warning.
272  wrappedDefaultComm_ = Teuchos::rcp (new Teuchos::MpiComm<int> (comm));
273  }
274 #endif // HAVE_TPETRACORE_MPI
275 
276  void
277  initialize (int* argc, char*** argv,
278  const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
279  {
280  if (! tpetraIsInitialized_) {
281 #if defined(HAVE_TPETRACORE_MPI)
282  initMpiIfNeeded (argc, argv);
283 #endif // defined(HAVE_TPETRACORE_MPI)
284  // It's technically legal to initialize Tpetra after
285  // MPI_Finalize has been called. This means that we can't call
286  // MPI_Comm_rank without first checking MPI_Finalized.
287  const int myRank = comm->getRank ();
288  initKokkosIfNeeded (argc, argv, myRank);
289 
290  // Add Kokkos calls to the TimeMonitor if the environment says so
291  Tpetra::Details::AddKokkosDeepCopyToTimeMonitor();
292  Tpetra::Details::AddKokkosFenceToTimeMonitor();
293  Tpetra::Details::AddKokkosFunctionsToTimeMonitor();
294 
296  }
297  tpetraIsInitialized_ = true;
298  wrappedDefaultComm_ = comm;
299  }
300 
301  void finalize ()
302  {
303  if (! tpetraIsInitialized_) {
304  return; // user didn't call initialize(), so do nothing at all
305  }
306 
307  // Tpetra should only finalize Kokkos if it initialized Kokkos.
308  // See Github Issue #434.
309  if (tpetraInitializedKokkos_ && !Kokkos::is_finalized()) {
310  Kokkos::finalize ();
311  }
312 
313  // Make sure that no outstanding references to the communicator
314  // remain. If users gave initialize() an MPI_Comm, _they_ are
315  // responsible for freeing it before calling finalize().
316  wrappedDefaultComm_ = Teuchos::null;
317 
318 #ifdef HAVE_TPETRACORE_MPI
319  // Tpetra should only finalize MPI if it initialized MPI.
320  // See Github Issue #434.
321  if (tpetraInitializedMpi_) {
322  // finalize() is a kind of destructor, so it's a bad idea to
323  // throw an exception on error. MPI implementations do have
324  // the option to throw on error, so let's catch that here.
325  try {
326  if (Details::mpiIsInitialized ()) {
327  // This must be called by the same thread that called
328  // MPI_Init or MPI_Init_thread (possibly, but not
329  // necessarily, in Tpetra::initialize()).
330  (void) MPI_Finalize ();
331  }
332  }
333  catch (...) {}
334  }
335 #endif // HAVE_TPETRACORE_MPI
336 
337  tpetraIsInitialized_ = false; // it's not anymore.
338  }
339 
340  ScopeGuard::ScopeGuard (int* argc, char*** argv)
341  {
342  ::Tpetra::initialize (argc, argv);
343  }
344 
345 #ifdef HAVE_TPETRA_MPI
346  ScopeGuard::ScopeGuard (int* argc, char*** argv, MPI_Comm comm)
347  {
348  ::Tpetra::initialize (argc, argv, comm);
349  }
350 #endif // HAVE_TPETRA_MPI
351 
353  {
354  ::Tpetra::finalize ();
355  }
356 
357 } // namespace Tpetra
~ScopeGuard()
Finalize Tpetra.
bool isInitialized()
Whether Tpetra is in an initialized state.
void initialize(int *argc, char ***argv)
Initialize Tpetra.
static void reject_unrecognized_env_vars()
Search the environment for TPETRA_ variables and reject unrecognized ones.
Declaration functions that use Kokkos&#39; profiling library to add deep copies between memory spaces...
void finalize()
Finalize Tpetra.
Functions for initializing and finalizing Tpetra.
bool mpiIsInitialized()
Has MPI_Init been called (on this process)?
Teuchos::RCP< const Teuchos::Comm< int > > getDefaultComm()
Get Tpetra&#39;s default communicator.
ScopeGuard()=delete
Default constructor (FORBIDDEN)
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra&#39;s behavior.