Actual source code: syclcontext.sycl.cxx
1: #include "sycldevice.hpp"
2: #include <CL/sycl.hpp>
3: #include <Kokkos_Core.hpp>
5: namespace Petsc
6: {
8: namespace device
9: {
11: namespace sycl
12: {
14: namespace impl
15: {
17: class DeviceContext {
18: public:
19: struct PetscDeviceContext_SYCL {
20: ::sycl::event event;
21: ::sycl::event begin; // timer-only
22: ::sycl::event end; // timer-only
23: Kokkos::Timer timer{}; // use cpu time since sycl events are return value of queue submission and we have no infrastructure to store them
24: double timeBegin{};
25: #if PetscDefined(USE_DEBUG)
26: PetscBool timerInUse{};
27: #endif
28: ::sycl::queue queue;
29: };
31: private:
32: static bool initialized_;
34: static PetscErrorCode finalize_() noexcept
35: {
36: PetscFunctionBegin;
37: initialized_ = false;
38: PetscFunctionReturn(PETSC_SUCCESS);
39: }
41: static PetscErrorCode initialize_(PetscInt id, PetscDeviceContext dctx) noexcept
42: {
43: PetscFunctionBegin;
44: PetscCall(PetscDeviceCheckDeviceCount_Internal(id));
45: if (!initialized_) {
46: initialized_ = true;
47: PetscCall(PetscRegisterFinalize(finalize_));
48: }
49: PetscFunctionReturn(PETSC_SUCCESS);
50: }
52: public:
53: const struct _DeviceContextOps ops = {destroy, changeStreamType, setUp, query, waitForContext, synchronize, getBlasHandle, getSolverHandle, getStreamHandle, beginTimer, endTimer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
55: // default constructor
56: DeviceContext() noexcept = default;
58: // All of these functions MUST be static in order to be callable from C, otherwise they
59: // get the implicit 'this' pointer tacked on
60: static PetscErrorCode destroy(PetscDeviceContext dctx) noexcept
61: {
62: PetscFunctionBegin;
63: delete static_cast<PetscDeviceContext_SYCL *>(dctx->data);
64: dctx->data = nullptr;
65: PetscFunctionReturn(PETSC_SUCCESS);
66: };
68: static PetscErrorCode setUp(PetscDeviceContext dctx) noexcept
69: {
70: PetscFunctionBegin;
71: #if PetscDefined(USE_DEBUG)
72: static_cast<PetscDeviceContext_SYCL *>(dctx->data)->timerInUse = PETSC_FALSE;
73: #endif
74: // petsc/sycl currently only uses Kokkos's default execution space (and its queue),
75: // so in some sense, we have only one petsc device context.
76: PetscCall(PetscKokkosInitializeCheck());
77: static_cast<PetscDeviceContext_SYCL *>(dctx->data)->queue = Kokkos::DefaultExecutionSpace().sycl_queue();
78: PetscFunctionReturn(PETSC_SUCCESS);
79: };
81: static PetscErrorCode query(PetscDeviceContext dctx, PetscBool *idle) noexcept
82: {
83: PetscFunctionBegin;
84: // available in future, https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_empty.asciidoc
85: // *idle = static_cast<PetscDeviceContext_SYCL*>(dctx->data)->queue.empty() ? PETSC_TRUE : PETSC_FALSE;
86: *idle = PETSC_FALSE;
87: PetscFunctionReturn(PETSC_SUCCESS);
88: };
90: static PetscErrorCode synchronize(PetscDeviceContext dctx) noexcept
91: {
92: PetscBool idle = PETSC_TRUE;
93: const auto dci = static_cast<PetscDeviceContext_SYCL *>(dctx->data);
95: PetscFunctionBegin;
96: PetscCall(query(dctx, &idle));
97: if (!idle) PetscCallCXX(dci->queue.wait());
98: PetscFunctionReturn(PETSC_SUCCESS);
99: };
101: static PetscErrorCode getStreamHandle(PetscDeviceContext dctx, void **handle) noexcept
102: {
103: PetscFunctionBegin;
104: *reinterpret_cast<::sycl::queue **>(handle) = &(static_cast<PetscDeviceContext_SYCL *>(dctx->data)->queue);
105: PetscFunctionReturn(PETSC_SUCCESS);
106: };
108: static PetscErrorCode beginTimer(PetscDeviceContext dctx) noexcept
109: {
110: const auto dci = static_cast<PetscDeviceContext_SYCL *>(dctx->data);
112: PetscFunctionBegin;
113: #if PetscDefined(USE_DEBUG)
114: PetscCheck(!dci->timerInUse, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Forgot to call PetscLogGpuTimeEnd()?");
115: dci->timerInUse = PETSC_TRUE;
116: #endif
117: PetscCallCXX(dci->timeBegin = dci->timer.seconds());
118: PetscFunctionReturn(PETSC_SUCCESS);
119: };
121: static PetscErrorCode endTimer(PetscDeviceContext dctx, PetscLogDouble *elapsed) noexcept
122: {
123: const auto dci = static_cast<PetscDeviceContext_SYCL *>(dctx->data);
125: PetscFunctionBegin;
126: #if PetscDefined(USE_DEBUG)
127: PetscCheck(dci->timerInUse, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Forgot to call PetscLogGpuTimeBegin()?");
128: dci->timerInUse = PETSC_FALSE;
129: #endif
130: PetscCallCXX(dci->queue.wait());
131: PetscCallCXX(*elapsed = dci->timer.seconds() - dci->timeBegin);
132: PetscFunctionReturn(PETSC_SUCCESS);
133: };
135: static PetscErrorCode changeStreamType(PetscDeviceContext, PetscStreamType) noexcept { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Not implemented"); };
136: static PetscErrorCode waitForContext(PetscDeviceContext, PetscDeviceContext) noexcept { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Not implemented"); };
137: static PetscErrorCode getBlasHandle(PetscDeviceContext, void *) noexcept { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Not implemented"); };
138: static PetscErrorCode getSolverHandle(PetscDeviceContext, void *) noexcept { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Not implemented"); };
139: };
141: } // namespace impl
143: } // namespace sycl
145: } // namespace device
147: } // namespace Petsc
149: PetscErrorCode PetscDeviceContextCreate_SYCL(PetscDeviceContext dctx)
150: {
151: using namespace Petsc::device::sycl::impl;
153: static const DeviceContext syclctx;
155: PetscFunctionBegin;
156: PetscCallCXX(dctx->data = new DeviceContext::PetscDeviceContext_SYCL());
157: dctx->ops[0] = syclctx.ops;
158: PetscFunctionReturn(PETSC_SUCCESS);
159: }