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