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