Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_Details_ExecutionSpaces.hpp
Go to the documentation of this file.
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#ifndef TPETRA_DETAILS_EXECUTIONSPACES_HPP
11#define TPETRA_DETAILS_EXECUTIONSPACES_HPP
12
13#include <iostream>
14#include <sstream>
15#include <vector>
16
17#include <Kokkos_Core.hpp>
18
19#include <Teuchos_RCP.hpp>
20
23#include "Tpetra_DLLExportMacro.h"
24
41#define TPETRA_DETAILS_SPACES_THROW(x) \
42 { \
43 std::stringstream ss; \
44 ss << __FILE__ << ":" << __LINE__ << ": " << x; \
45 throw std::runtime_error(ss.str()); \
46 }
47
48namespace Tpetra {
49namespace Details {
50namespace Spaces {
51
57enum class Priority {
58 low = 0,
59 medium = 1,
60 high = 2,
61 NUM_LEVELS = 3 // not to be used as a priority
62};
63
64#if defined(KOKKOS_ENABLE_CUDA)
65inline void success_or_throw(cudaError_t err, const char *file,
66 const int line) {
67 if (err != cudaSuccess) {
68 std::stringstream ss;
69 ss << file << ":" << line << ": ";
71 throw std::runtime_error(ss.str());
72 }
73}
74#define TPETRA_DETAILS_SPACES_CUDA_RUNTIME(x) \
75 Tpetra::Details::Spaces::success_or_throw((x), __FILE__, __LINE__)
76#endif // KOKKOS_ENABLE_CUDA
77
84void lazy_init();
85
86#if defined(KOKKOS_ENABLE_CUDA)
87struct CudaInfo {
88 bool initialized_;
89 int lowPrio_;
90 int mediumPrio_; // same as CUDA default
91 int highPrio_;
92 cudaEvent_t execSpaceWaitEvent_; // see exec_space_wait
93
94 CudaInfo();
95 ~CudaInfo() = default; // execSpaceWaitEvent_ cleaned up by CUDA deinit
96 CudaInfo(const CudaInfo &other) = delete;
97 CudaInfo(CudaInfo &&other) = delete;
98};
99extern TPETRACORE_LIB_DLL_EXPORT CudaInfo cudaInfo;
100#endif // KOKKOS_ENABLE_CUDA
101
102// Tpetra's managed spaces
103#if defined(KOKKOS_ENABLE_CUDA)
104template <typename Space>
105using IsCuda = std::enable_if_t<std::is_same_v<Space, Kokkos::Cuda>, bool>;
106template <typename Space>
107using NotCuda = std::enable_if_t<!std::is_same_v<Space, Kokkos::Cuda>, bool>;
108template <typename S1, typename S2>
109using BothCuda = std::enable_if_t<
110 std::is_same_v<S1, Kokkos::Cuda> && std::is_same_v<S2, Kokkos::Cuda>, bool>;
111template <typename S1, typename S2>
112using NotBothCuda = std::enable_if_t<!std::is_same_v<S1, Kokkos::Cuda> ||
113 !std::is_same_v<S2, Kokkos::Cuda>,
114 bool>;
115#endif // KOKKOS_ENABLE_CUDA
116
117#if defined(KOKKOS_ENABLE_SERIAL)
119template <typename Space>
120using IsSerial = std::enable_if_t<std::is_same_v<Space, Kokkos::Serial>, bool>;
121#endif // KOKKOS_ENABLE_SERIAL
122
123#if defined(KOKKOS_ENABLE_OPENMP)
125template <typename Space>
126using IsOpenMP = std::enable_if_t<std::is_same_v<Space, Kokkos::OpenMP>, bool>;
127#endif // KOKKOS_ENABLE_OPENMP
128
129#if defined(KOKKOS_ENABLE_HIP)
131template <typename Space>
132using IsHIP = std::enable_if_t<std::is_same_v<Space, Kokkos::HIP>, bool>;
133#endif // KOKKOS_ENABLE_HIP
134
135#if defined(KOKKOS_ENABLE_SYCL)
137template <typename Space>
138using IsSYCL = std::enable_if_t<std::is_same_v<Space, Kokkos::SYCL>, bool>;
139#endif // KOKKOS_ENABLE_SYCL
140
146template <typename ExecSpace, Priority priority = Priority::medium
147#if defined(KOKKOS_ENABLE_CUDA)
148 ,
149 NotCuda<ExecSpace> = true
150#endif // KOKKOS_ENABLE_CUDA
151 >
153 return ExecSpace();
154}
155
162#if defined(KOKKOS_ENABLE_CUDA)
163template <typename ExecSpace, Priority priority = Priority::medium,
164 IsCuda<ExecSpace> = true>
165Kokkos::Cuda make_instance() {
166 lazy_init(); // CUDA priorities
168 int prio;
169 switch (priority) {
170 case Priority::high:
171 prio = cudaInfo.highPrio_;
172 break;
173 case Priority::medium:
174 prio = cudaInfo.mediumPrio_;
175 break;
176 case Priority::low:
177 prio = cudaInfo.lowPrio_;
178 break;
179 default:
180 throw std::runtime_error("unexpected static Tpetra Space priority");
181 }
184
185 Kokkos::push_finalize_hook([=] {
186 if (stream != nullptr) {
189 }
190 });
191
192 return Kokkos::Cuda(stream);
193}
194#endif // KOKKOS_ENABLE_CUDA
195
201template <typename ExecSpace>
203 switch (prio) {
204 case Priority::high:
206 case Priority::medium:
208 case Priority::low:
210 default:
211 throw std::runtime_error("unexpected dynamic Tpetra Space priority");
212 }
213}
214
226template <typename ExecSpace>
228 public:
230 using rcp_type = Teuchos::RCP<const execution_space>;
231
238 template <Priority priority = Priority::medium>
239 rcp_type space_instance(int i = 0) {
241 "Tpetra::Details::Spaces::space_instance");
242
243 constexpr int p = static_cast<int>(priority);
244 static_assert(p < sizeof(instances) / sizeof(instances[0]),
245 "Spaces::Priority enum error");
246
247 if (i < 0) {
248 TPETRA_DETAILS_SPACES_THROW("requested instance id " << i << " (< 0)");
249 }
251 TPETRA_DETAILS_SPACES_THROW(
252 "requested instance id "
254 << ") set by TPETRA_SPACES_ID_WARN_LIMIT");
255 }
256
257 // make sure we can store an exec space at index i for priority
258 // not sure what happens in RCP(), so let's explicitly make it null
259 while (size_t(i) >= instances[p].size()) {
260 instances[p].push_back(Teuchos::ENull());
261 }
262
263 /* no exec space instance i of priority p exists.
264 It may have never existed, or all Tpetra objects referencing it have been
265 destructed.
266
267 Create a new RCP<ExecSpace> and internally store a weak
268 reference, so this space will be destructed when all strong references to
269 it are gone, but we can still refer to it as long as it lives to prevent
270 recreating
271 */
272 if (instances[p][i].is_null() || !instances[p][i].is_valid_ptr()) {
273 // create a strong RCP to a space
274 rcp_type r = Teuchos::RCP<const execution_space>(
276
277 // store a weak RCP to the space
278 instances[p][i] = r.create_weak();
279
280 return r; // allow strong rcp to escape so internal weak one does not
281 // immediately go away
282 }
283
284 auto r = instances[p][i].create_strong();
285 return r;
286 }
287
292 for (int i = 0; i < static_cast<int>(Spaces::Priority::NUM_LEVELS); ++i) {
293 for (const rcp_type &rcp : instances[i]) {
294 if (rcp.is_valid_ptr() && !rcp.is_null()) {
295 // avoid throwing in dtor
296 std::cerr << __FILE__ << ":" << __LINE__
297 << " execution space instance survived to "
298 "~InstanceLifetimeManager. strong_count() = "
299 << rcp.strong_count()
300 << ". Did a Tpetra object live past Kokkos::finalize()?"
301 << std::endl;
302 }
303 }
304 }
305 }
306
307 private:
308 // one vector of instances for each priority level
309 std::vector<rcp_type>
310 instances[static_cast<int>(Spaces::Priority::NUM_LEVELS)];
311};
312
313#if defined(KOKKOS_ENABLE_CUDA)
315#endif
316#if defined(KOKKOS_ENABLE_SERIAL)
318#endif
319#if defined(KOKKOS_ENABLE_OPENMP)
321#endif
322#if defined(KOKKOS_ENABLE_HIP)
324#endif
325#if defined(KOKKOS_ENABLE_SYCL)
327#endif
328
329#if defined(KOKKOS_ENABLE_CUDA)
330
334template <typename ExecSpace, Priority priority = Priority::medium,
335 IsCuda<ExecSpace> = true>
336Teuchos::RCP<const ExecSpace> space_instance(int i = 0) {
337 return cudaSpaces.space_instance<priority>(i);
338}
339#endif
340
341#if defined(KOKKOS_ENABLE_SERIAL)
345template <typename ExecSpace, Priority priority = Priority::medium,
346 IsSerial<ExecSpace> = true>
347Teuchos::RCP<const ExecSpace> space_instance(int i = 0) {
348 return serialSpaces.space_instance<priority>(i);
349}
350#endif
351
352#if defined(KOKKOS_ENABLE_OPENMP)
356template <typename ExecSpace, Priority priority = Priority::medium,
357 IsOpenMP<ExecSpace> = true>
358Teuchos::RCP<const ExecSpace> space_instance(int i = 0) {
359 return openMPSpaces.space_instance<priority>(i);
360}
361#endif
362
363#if defined(KOKKOS_ENABLE_HIP)
366template <typename ExecSpace, Priority priority = Priority::medium,
367 IsHIP<ExecSpace> = true>
368Teuchos::RCP<const ExecSpace> space_instance(int i = 0) {
369 return HIPSpaces.space_instance<priority>(i);
370}
371#endif
372#if defined(KOKKOS_ENABLE_SYCL)
376template <typename ExecSpace, Priority priority = Priority::medium,
377 IsSYCL<ExecSpace> = true>
378Teuchos::RCP<const ExecSpace> space_instance(int i = 0) {
379 return SYCLSpaces.space_instance<priority>(i);
380}
381#endif
382
389template <typename ExecSpace>
390Teuchos::RCP<const ExecSpace> space_instance(const Priority &priority,
391 int i = 0) {
392 switch (priority) {
393 case Priority::high:
395 case Priority::medium:
397 case Priority::low:
399 default:
400 throw std::runtime_error(
401 "unexpected dynamic Tpetra Space priority in space_instance");
402 }
403}
404
418template <typename S1, typename S2
419#if defined(KOKKOS_ENABLE_CUDA)
420 ,
422#endif
423 >
424void exec_space_wait(const char *msg, const S1 &waitee, const S2 & /*waiter*/) {
426 "Tpetra::Details::Spaces::exec_space_wait");
427 lazy_init();
428 waitee.fence(msg);
429}
430
431#if defined(KOKKOS_ENABLE_CUDA)
432template <typename S1, typename S2, BothCuda<S1, S2> = true>
433void exec_space_wait(const char *msg, const S1 &waitee, const S2 &waiter) {
435 "Tpetra::Details::Spaces::exec_space_wait");
436 lazy_init();
437
438 // if they are the same instance, no sync needed
439 if (waitee.impl_instance_id() !=
440 waiter
441 .impl_instance_id()) { // TODO: use instance operator== once available
442 /* cudaStreamWaitEvent is not affected by later calls to cudaEventRecord,
443 even if it overwrites the state of a shared event this means we only need
444 one event even if many exec_space_waits are in flight at the same time
445 */
447 cudaEventRecord(cudaInfo.execSpaceWaitEvent_, waitee.cuda_stream()));
449 waiter.cuda_stream(), cudaInfo.execSpaceWaitEvent_, 0 /*flags*/));
450 }
451}
452#endif
453
454template <typename S1, typename S2>
455void exec_space_wait(const S1 &waitee, const S2 &waiter) {
457 "Tpetra::Details::Spaces::exec_space_wait");
458 lazy_init();
459 exec_space_wait("anonymous", waitee, waiter);
460}
461
462template <typename ExecutionSpace>
463constexpr KOKKOS_INLINE_FUNCTION bool is_gpu_exec_space() {
464 return false;
465}
466
467#if defined(KOKKOS_ENABLE_CUDA)
468template <>
469constexpr KOKKOS_INLINE_FUNCTION bool is_gpu_exec_space<Kokkos::Cuda>() {
470 return true;
471}
472#endif
473
474#if defined(KOKKOS_ENABLE_HIP)
475template <>
476constexpr KOKKOS_INLINE_FUNCTION bool
477is_gpu_exec_space<Kokkos::HIP>() {
478 return true;
479}
480#endif
481
482#if defined(KOKKOS_ENABLE_SYCL)
483template <>
484constexpr KOKKOS_INLINE_FUNCTION bool
485is_gpu_exec_space<Kokkos::SYCL>() {
486 return true;
487}
488#endif
489
490} // namespace Spaces
491} // namespace Details
492} // namespace Tpetra
493
494#undef TPETRA_DETAILS_SPACES_THROW
495
496#endif // TPETRA_DETAILS_EXECUTIONSPACES_HPP
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
void exec_space_wait(const char *msg, const S1 &waitee, const S2 &)
cause future work submitted to waiter to wait for the current work in waitee to finish
ExecSpace make_instance()
Construct a Kokkos execution space instance with the following priority.
Teuchos::RCP< const ExecSpace > space_instance(const Priority &priority, int i=0)
get a strong Teuchos::RCP to Tpetra-managed Kokkos execution space instance
Priority
Priority interface for Tpetra's managed execution spaces.
Declaration of Tpetra::Details::Profiling, a scope guard for Kokkos Profiling.
Struct that holds views of the contents of a CrsMatrix.
static size_t spacesIdWarnLimit()
Warn if more than this many Kokkos spaces are accessed.
Provides reusable Kokkos execution space instances.
~InstanceLifetimeManager()
Issue a warning if any Tpetra-managed execution space instances survive to the end of static lifetime...
rcp_type space_instance(int i=0)
Retrieve a strong Teuchos::RCP<const ExecSpace> to instance i
Implementation details of Tpetra.
Namespace Tpetra contains the class and methods constituting the Tpetra library.