Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
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
24namespace 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
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 {
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
360
361} // namespace Tpetra
Functions for initializing and finalizing Tpetra.
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
Declaration functions that use Kokkos' profiling library to add deep copies between memory spaces,...
static void reject_unrecognized_env_vars()
Search the environment for TPETRA_ variables and reject unrecognized ones.
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.