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