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 //
4 // Tpetra: Templated Linear Algebra Services Package
5 // Copyright (2008) Sandia Corporation
6 //
7 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8 // the U.S. Government retains certain rights in this software.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // ************************************************************************
38 // @HEADER
39 
40 #include "Tpetra_Core.hpp"
41 #include "Tpetra_Details_mpiIsInitialized.hpp"
42 
43 #ifdef HAVE_TPETRACORE_MPI
44 # include <Teuchos_DefaultMpiComm.hpp> // this includes mpi.h too
45 #endif // HAVE_TPETRACORE_MPI
46 #include <Teuchos_DefaultSerialComm.hpp>
47 
48 #include <Kokkos_Core.hpp>
49 #include "Tpetra_Details_checkLaunchBlocking.hpp"
52 
53 namespace Tpetra {
54 
55  namespace { // (anonymous)
56 
57  class HideOutputExceptOnProcess0 {
58  public:
59  HideOutputExceptOnProcess0 (std::ostream& stream,
60  const int myRank) :
61  stream_ (stream),
62  originalBuffer_ (stream.rdbuf ())
63  {
64  if (myRank != 0) {
65  stream.rdbuf (blackHole_.rdbuf ());
66  }
67  }
68 
69  ~HideOutputExceptOnProcess0 () {
70  stream_.rdbuf (originalBuffer_);
71  }
72  private:
73  std::ostream& stream_;
74  decltype (std::cout.rdbuf ()) originalBuffer_;
75  Teuchos::oblackholestream blackHole_;
76  };
77 
78 #if defined(HAVE_TPETRACORE_MPI)
79  bool mpiIsInitializedAndNotFinalized ()
80  {
81  int isInitialized = 0;
82  int isFinalized = 0;
83  // Not sure if MPI_Initialized or MPI_Finalized meet the strong
84  // exception guarantee.
85  try {
86  (void) MPI_Initialized (&isInitialized);
87  }
88  catch (...) {
89  isInitialized = 0;
90  }
91  try {
92  (void) MPI_Finalized (&isFinalized);
93  }
94  catch (...) {
95  isFinalized = 0;
96  }
97  return isInitialized != 0 && isFinalized == 0;
98  }
99 
100  int getRankHarmlessly (MPI_Comm comm)
101  {
102  int myRank = 0;
103  if (mpiIsInitializedAndNotFinalized ()) {
104  try {
105  (void) MPI_Comm_rank (comm, &myRank);
106  }
107  catch (...) {
108  // Not sure if MPI_Comm_rank meets strong exception guarantee
109  myRank = 0;
110  }
111  }
112  return myRank;
113  }
114 #endif // defined(HAVE_TPETRACORE_MPI)
115 
116 
117  // Whether one of the Tpetra::initialize() functions has been called before.
118  bool tpetraIsInitialized_ = false;
119 
120  // Whether Tpetra initialized Kokkos. Tpetra::finalize only
121  // finalizes Kokkos if it initialized Kokkos. Otherwise,
122  // something else initialized Kokkos and is responsible for
123  // finalizing it.
124  bool tpetraInitializedKokkos_ = false;
125 
126 #ifdef HAVE_TPETRACORE_MPI
127  // Whether Tpetra initialized MPI. Tpetra::finalize only
128  // finalizes MPI if it initialized MPI. Otherwise, something else
129  // initialized MPI and is responsible for finalizing it.
130  bool tpetraInitializedMpi_ = false;
131 #endif // HAVE_TPETRACORE_MPI
132 
133  // Tpetra's default communicator, wrapped in a Teuchos wrapper.
134  // After Tpetra::finalize() is called, this GOES AWAY (is set to null).
135  Teuchos::RCP<const Teuchos::Comm<int> > wrappedDefaultComm_;
136 
137  // This takes the same arguments as (the first two of) initialize().
138  void initKokkosIfNeeded (int* argc, char*** argv, const int myRank)
139  {
140  if (! tpetraInitializedKokkos_) {
141  // Kokkos doesn't have a global is_initialized(). However,
142  // Kokkos::initialize() always initializes the default execution
143  // space, so it suffices to check whether that was initialized.
144  const bool kokkosIsInitialized =
145  Kokkos::is_initialized ();
146  if (! kokkosIsInitialized) {
147  HideOutputExceptOnProcess0 hideCerr (std::cerr, myRank);
148  HideOutputExceptOnProcess0 hideCout (std::cout, myRank);
149 
150  // Unlike MPI_Init, Kokkos promises not to modify argc and argv.
151  Kokkos::initialize (*argc, *argv);
152  tpetraInitializedKokkos_ = true;
153  }
154  }
155  Details::checkOldCudaLaunchBlocking();
156  const bool kokkosIsInitialized =
157  Kokkos::is_initialized ();
158  TEUCHOS_TEST_FOR_EXCEPTION
159  (! kokkosIsInitialized, std::logic_error, "At the end of "
160  "initKokkosIfNeeded, Kokkos is not initialized. "
161  "Please report this bug to the Tpetra developers.");
162  }
163 
164 #ifdef HAVE_TPETRACORE_MPI
165  // This takes the same arguments as MPI_Init and the first two
166  // arguments of initialize().
167  void initMpiIfNeeded (int* argc, char*** argv)
168  {
169  // Both MPI_Initialized and MPI_Finalized report true after
170  // MPI_Finalize has been called. It's not legal to call
171  // MPI_Init after MPI_Finalize has been called (see MPI 3.0
172  // Standard, Section 8.7). It would be unusual for users to
173  // want to use Tpetra after MPI_Finalize has been called, but
174  // there's no reason why we should forbid it. It just means
175  // that Tpetra will need to run without MPI.
176 
177  const bool mpiReady = mpiIsInitializedAndNotFinalized ();
178  if (! mpiReady) {
179  // Tpetra doesn't currently need to call MPI_Init_thread,
180  // since with Tpetra, only one thread ever calls MPI
181  // functions. If we ever want to explore
182  // MPI_THREAD_MULTIPLE, here would be the place to call
183  // MPI_Init_thread.
184  const int err = MPI_Init (argc, argv);
185  TEUCHOS_TEST_FOR_EXCEPTION
186  (err != MPI_SUCCESS, std::runtime_error, "MPI_Init failed with "
187  "error code " << err << " != MPI_SUCCESS. If MPI was set up "
188  "correctly, then this should not happen, since we have already "
189  "checked that MPI_Init (or MPI_Init_thread) has not yet been "
190  "called. This may indicate that your MPI library is corrupted "
191  "or that it is incorrectly linked to your program.");
192  tpetraInitializedMpi_ = true;
193  }
194  }
195 #endif // HAVE_TPETRACORE_MPI
196 
197  } // namespace (anonymous)
198 
199  bool isInitialized () {
200  return tpetraIsInitialized_;
201  }
202 
203  Teuchos::RCP<const Teuchos::Comm<int> > getDefaultComm ()
204  {
205  // It's technically not correct to call this function if Tpetra
206  // has not yet been initialized, but requiring that may break some
207  // downstream tests.
208  //
209  // This function initializes wrappedDefaultComm_ lazily.
210  // Tpetra::initialize should not set it up.
211  if (wrappedDefaultComm_.is_null ()) {
212  Teuchos::RCP<const Teuchos::Comm<int> > comm;
213 #ifdef HAVE_TPETRACORE_MPI
214  // Teuchos::MpiComm's constructor used to invoke MPI collectives.
215  // It still reserves the right to do so. This means MPI must be
216  // initialized and not finalized.
217  const bool mpiReady = mpiIsInitializedAndNotFinalized ();
218  if (mpiReady) {
219  comm = Teuchos::rcp (new Teuchos::MpiComm<int> (MPI_COMM_WORLD));
220  }
221  else {
222  comm = Teuchos::rcp (new Teuchos::SerialComm<int> ());
223  }
224 #else
225  comm = Teuchos::rcp (new Teuchos::SerialComm<int> ());
226 #endif // HAVE_TPETRACORE_MPI
227  wrappedDefaultComm_ = comm;
228  }
229  return wrappedDefaultComm_;
230  }
231 
232  void initialize (int* argc, char*** argv)
233  {
234  if (! tpetraIsInitialized_) {
235 #if defined(HAVE_TPETRACORE_MPI)
236  initMpiIfNeeded (argc, argv);
237  // It's technically legal to initialize Tpetra after
238  // MPI_Finalize has been called. This means that we can't call
239  // MPI_Comm_rank without first checking MPI_Finalized.
240  const int myRank = getRankHarmlessly (MPI_COMM_WORLD);
241 #else
242  const int myRank = 0;
243 #endif // defined(HAVE_TPETRACORE_MPI)
244  initKokkosIfNeeded (argc, argv, myRank);
245 
246  // Add Kokkos calls to the TimeMonitor if the environment says so
247  Tpetra::Details::AddKokkosDeepCopyToTimeMonitor();
248  Tpetra::Details::AddKokkosFenceToTimeMonitor();
249  Tpetra::Details::AddKokkosFunctionsToTimeMonitor();
250 
252  }
253  tpetraIsInitialized_ = true;
254  }
255 
256 #ifdef HAVE_TPETRACORE_MPI
257  void initialize (int* argc, char*** argv, MPI_Comm comm)
258  {
259  if (! tpetraIsInitialized_) {
260 #if defined(HAVE_TPETRACORE_MPI)
261  initMpiIfNeeded (argc, argv);
262  // It's technically legal to initialize Tpetra after
263  // MPI_Finalize has been called. This means that we can't call
264  // MPI_Comm_rank without first checking MPI_Finalized.
265  const int myRank = getRankHarmlessly (comm);
266 #else
267  const int myRank = 0;
268 #endif // defined(HAVE_TPETRACORE_MPI)
269  initKokkosIfNeeded (argc, argv, myRank);
270 
271  // Add Kokkos::deep calls to the TimeMonitor if the environment says so
272  Tpetra::Details::AddKokkosDeepCopyToTimeMonitor();
273  Tpetra::Details::AddKokkosFenceToTimeMonitor();
274  Tpetra::Details::AddKokkosFunctionsToTimeMonitor();
275 
277  }
278  tpetraIsInitialized_ = true;
279 
280  // Set the default communicator. We set it here, after the above
281  // initialize() call, just in case users have not yet initialized
282  // MPI. (This is legal if users pass in a predefined
283  // communicator, like MPI_COMM_WORLD or MPI_COMM_SELF.)
284  //
285  // What if users have already called initialize() before, but with
286  // a different default communicator? There are two possible
287  // things we could do here:
288  //
289  // 1. Test via MPI_Comm_compare whether comm differs from the
290  // raw MPI communicator in wrappedDefaultComm_ (if indeed it
291  // is an MpiComm).
292  // 2. Accept that the user might want to change the default
293  // communicator, and let them do it.
294  //
295  // I prefer #2. Perhaps it would be sensible to print a warning
296  // here, but on which process? Would we use the old or the new
297  // communicator to find that process' rank? We don't want to use
298  // MPI_COMM_WORLD's Process 0, since neither communicator might
299  // include that process. Furthermore, in some environments, only
300  // Process 0 in MPI_COMM_WORLD is allowed to do I/O. Thus, we
301  // just let the change go without a warning.
302  wrappedDefaultComm_ = Teuchos::rcp (new Teuchos::MpiComm<int> (comm));
303  }
304 #endif // HAVE_TPETRACORE_MPI
305 
306  void
307  initialize (int* argc, char*** argv,
308  const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
309  {
310  if (! tpetraIsInitialized_) {
311 #if defined(HAVE_TPETRACORE_MPI)
312  initMpiIfNeeded (argc, argv);
313 #endif // defined(HAVE_TPETRACORE_MPI)
314  // It's technically legal to initialize Tpetra after
315  // MPI_Finalize has been called. This means that we can't call
316  // MPI_Comm_rank without first checking MPI_Finalized.
317  const int myRank = comm->getRank ();
318  initKokkosIfNeeded (argc, argv, myRank);
319 
320  // Add Kokkos calls to the TimeMonitor if the environment says so
321  Tpetra::Details::AddKokkosDeepCopyToTimeMonitor();
322  Tpetra::Details::AddKokkosFenceToTimeMonitor();
323  Tpetra::Details::AddKokkosFunctionsToTimeMonitor();
324 
326  }
327  tpetraIsInitialized_ = true;
328  wrappedDefaultComm_ = comm;
329  }
330 
331  void finalize ()
332  {
333  if (! tpetraIsInitialized_) {
334  return; // user didn't call initialize(), so do nothing at all
335  }
336 
337  // Tpetra should only finalize Kokkos if it initialized Kokkos.
338  // See Github Issue #434.
339  if (tpetraInitializedKokkos_ && !Kokkos::is_finalized()) {
340  Kokkos::finalize ();
341  }
342 
343  // Make sure that no outstanding references to the communicator
344  // remain. If users gave initialize() an MPI_Comm, _they_ are
345  // responsible for freeing it before calling finalize().
346  wrappedDefaultComm_ = Teuchos::null;
347 
348 #ifdef HAVE_TPETRACORE_MPI
349  // Tpetra should only finalize MPI if it initialized MPI.
350  // See Github Issue #434.
351  if (tpetraInitializedMpi_) {
352  // finalize() is a kind of destructor, so it's a bad idea to
353  // throw an exception on error. MPI implementations do have
354  // the option to throw on error, so let's catch that here.
355  try {
356  if (Details::mpiIsInitialized ()) {
357  // This must be called by the same thread that called
358  // MPI_Init or MPI_Init_thread (possibly, but not
359  // necessarily, in Tpetra::initialize()).
360  (void) MPI_Finalize ();
361  }
362  }
363  catch (...) {}
364  }
365 #endif // HAVE_TPETRACORE_MPI
366 
367  tpetraIsInitialized_ = false; // it's not anymore.
368  }
369 
370  ScopeGuard::ScopeGuard (int* argc, char*** argv)
371  {
372  ::Tpetra::initialize (argc, argv);
373  }
374 
375 #ifdef HAVE_TPETRA_MPI
376  ScopeGuard::ScopeGuard (int* argc, char*** argv, MPI_Comm comm)
377  {
378  ::Tpetra::initialize (argc, argv, comm);
379  }
380 #endif // HAVE_TPETRA_MPI
381 
383  {
384  ::Tpetra::finalize ();
385  }
386 
387 } // 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.