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
26namespace { // (anonymous)
27
28class 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)
50bool 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
68int 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.
83bool 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.
89bool 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.
95bool 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).
100Teuchos::RCP<const Teuchos::Comm<int> > wrappedDefaultComm_;
101
102// This takes the same arguments as (the first two of) initialize().
103void 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().
134void 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
168}
169
170Teuchos::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.
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
197void initialize(int* argc, char*** argv) {
199#if defined(HAVE_TPETRACORE_MPI)
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.
205#else
206 const int myRank = 0;
207#endif // defined(HAVE_TPETRACORE_MPI)
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 }
218}
219
220#ifdef HAVE_TPETRACORE_MPI
221void 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
269void initialize(int* argc, char*** argv,
270 const Teuchos::RCP<const Teuchos::Comm<int> >& comm) {
272#if defined(HAVE_TPETRACORE_MPI)
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();
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 }
289 wrappedDefaultComm_ = comm;
290}
291
292void finalize() {
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.
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()).
321 }
322 } catch (...) {
323 }
324 }
325#endif // HAVE_TPETRACORE_MPI
326
327 tpetraIsInitialized_ = false; // it's not anymore.
328}
329
333
334#ifdef HAVE_TPETRA_MPI
335ScopeGuard::ScopeGuard(int* argc, char*** argv, MPI_Comm comm) {
337}
338#endif // HAVE_TPETRA_MPI
339
343
344} // 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,...
Struct that holds views of the contents of a CrsMatrix.
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.