Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_device.F90
Go to the documentation of this file.
1! Copyright (c) 2021-2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
35 use num_types, only : rp, c_rp
36 use gs_bcknd, only : gs_bcknd_t
41 use utils, only : neko_error
42 use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_null_ptr, &
43 c_associated, c_sizeof, c_size_t
44 implicit none
45 private
46
48 type, public, extends(gs_bcknd_t) :: gs_device_t
49 type(c_ptr) :: local_gs_d = c_null_ptr
50 type(c_ptr) :: local_dof_gs_d = c_null_ptr
51 type(c_ptr) :: local_gs_dof_d = c_null_ptr
52 type(c_ptr) :: shared_gs_d = c_null_ptr
53 type(c_ptr) :: shared_dof_gs_d = c_null_ptr
54 type(c_ptr) :: shared_gs_dof_d = c_null_ptr
55 type(c_ptr) :: local_blk_len_d = c_null_ptr
56 type(c_ptr) :: shared_blk_len_d = c_null_ptr
57 type(c_ptr) :: local_blk_off_d = c_null_ptr
58 type(c_ptr) :: shared_blk_off_d = c_null_ptr
59 integer :: nlocal
60 integer :: nshared
61 contains
62 procedure, pass(this) :: init => gs_device_init
63 procedure, pass(this) :: free => gs_device_free
64 procedure, pass(this) :: gather => gs_gather_device
65 procedure, pass(this) :: scatter => gs_scatter_device
66 end type gs_device_t
67
68#ifdef HAVE_HIP
69 interface
70 subroutine hip_gather_kernel(v, m, o, dg, u, n, gd, nb, b, bo, op, strm) &
71 bind(c, name = 'hip_gather_kernel')
72 use, intrinsic :: iso_c_binding
73 implicit none
74 integer(c_int) :: m, n, nb, o, op
75 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
76 end subroutine hip_gather_kernel
77 end interface
78
79 interface
80 subroutine hip_scatter_kernel(v, m, dg, u, n, gd, nb, b, bo, strm) &
81 bind(c, name = 'hip_scatter_kernel')
82 use, intrinsic :: iso_c_binding
83 implicit none
84 integer(c_int) :: m, n, nb
85 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
86 end subroutine hip_scatter_kernel
87 end interface
88
89#elif HAVE_CUDA
90 interface
91 subroutine cuda_gather_kernel(v, m, o, dg, u, n, gd, nb, b, bo, op, strm) &
92 bind(c, name = 'cuda_gather_kernel')
93 use, intrinsic :: iso_c_binding
94 implicit none
95 integer(c_int) :: m, n, nb, o, op
96 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
97 end subroutine cuda_gather_kernel
98 end interface
99
100 interface
101 subroutine cuda_scatter_kernel(v, m, dg, u, n, gd, nb, b, bo, strm) &
102 bind(c, name = 'cuda_scatter_kernel')
103 use, intrinsic :: iso_c_binding
104 implicit none
105 integer(c_int) :: m, n, nb
106 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
107 end subroutine cuda_scatter_kernel
108 end interface
109#elif HAVE_OPENCL
110 interface
111 subroutine opencl_gather_kernel(v, m, o, dg, u, n, gd, nb, b, bo, op, &
112 strm) bind(c, name = 'opencl_gather_kernel')
113 use, intrinsic :: iso_c_binding
114 implicit none
115 integer(c_int) :: m, n, nb, o, op
116 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
117 end subroutine opencl_gather_kernel
118 end interface
119
120 interface
121 subroutine opencl_scatter_kernel(v, m, dg, u, n, gd, nb, b, bo, strm) &
122 bind(c, name = 'opencl_scatter_kernel')
123 use, intrinsic :: iso_c_binding
124 implicit none
125 integer(c_int) :: m, n, nb
126 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
127 end subroutine opencl_scatter_kernel
128 end interface
129#elif HAVE_METAL
130 interface
131 subroutine metal_gather_kernel(v, m, o, dg, u, n, gd, nb, b, bo, op, &
132 strm) bind(c, name = 'metal_gather_kernel')
133 use, intrinsic :: iso_c_binding
134 implicit none
135 integer(c_int) :: m, n, nb, o, op
136 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
137 end subroutine metal_gather_kernel
138 end interface
139
140 interface
141 subroutine metal_scatter_kernel(v, m, dg, u, n, gd, nb, b, bo, strm) &
142 bind(c, name = 'metal_scatter_kernel')
143 use, intrinsic :: iso_c_binding
144 implicit none
145 integer(c_int) :: m, n, nb
146 type(c_ptr), value :: v, u, dg, gd, b, bo, strm
147 end subroutine metal_scatter_kernel
148 end interface
149#endif
150
151contains
152
154 subroutine gs_device_init(this, nlocal, nshared, nlcl_blks, nshrd_blks)
155 class(gs_device_t), intent(inout) :: this
156 integer, intent(in) :: nlocal
157 integer, intent(in) :: nshared
158 integer, intent(in) :: nlcl_blks
159 integer, intent(in) :: nshrd_blks
160
161 call this%free()
162
163 this%nlocal = nlocal
164 this%nshared = nshared
165
166 this%local_gs_d = c_null_ptr
167 this%local_dof_gs_d = c_null_ptr
168 this%local_gs_dof_d = c_null_ptr
169 this%local_blk_len_d = c_null_ptr
170 this%local_blk_off_d = c_null_ptr
171 this%shared_gs_d = c_null_ptr
172 this%shared_dof_gs_d = c_null_ptr
173 this%shared_gs_dof_d = c_null_ptr
174 this%shared_blk_len_d = c_null_ptr
175 this%shared_blk_off_d = c_null_ptr
176
177 this%shared_on_host = .true.
178
179#if defined(HAVE_HIP) || defined(HAVE_CUDA) || defined(HAVE_METAL)
180 call device_event_create(this%gather_event, 2)
181 call device_event_create(this%scatter_event, 2)
182#endif
183
184 this%gs_stream = glb_cmd_queue
185
186 end subroutine gs_device_init
187
189 subroutine gs_device_free(this)
190 class(gs_device_t), intent(inout) :: this
191
192 if (c_associated(this%local_gs_d)) then
193 call device_free(this%local_gs_d)
194 end if
195
196 if (c_associated(this%local_dof_gs_d)) then
197 call device_free(this%local_dof_gs_d)
198 end if
199
200 if (c_associated(this%local_gs_dof_d)) then
201 call device_free(this%local_gs_dof_d)
202 end if
203
204 if (c_associated(this%local_blk_len_d)) then
205 call device_free(this%local_blk_len_d)
206 end if
207
208 if (c_associated(this%shared_blk_len_d)) then
209 call device_free(this%shared_blk_len_d)
210 end if
211
212 if (c_associated(this%local_blk_off_d)) then
213 call device_free(this%local_blk_off_d)
214 end if
215
216 if (c_associated(this%shared_blk_off_d)) then
217 call device_free(this%shared_blk_off_d)
218 end if
219
220 this%nlocal = 0
221 this%nshared = 0
222
223#if defined(HAVE_HIP) || defined(HAVE_CUDA) || defined(HAVE_METAL)
224 if (c_associated(this%gather_event)) then
225 call device_event_destroy(this%gather_event)
226 end if
227
228 if (c_associated(this%scatter_event)) then
229 call device_event_destroy(this%scatter_event)
230 end if
231#endif
232
233 if (c_associated(this%gs_stream)) then
234 this%gs_stream = c_null_ptr
235 end if
236
237 end subroutine gs_device_free
238
240 subroutine gs_gather_device(this, v, m, o, dg, u, n, gd, nb, b, bo, op, shrd)
241 integer, intent(in) :: m
242 integer, intent(in) :: n
243 integer, intent(in) :: nb
244 class(gs_device_t), intent(inout) :: this
245 real(kind=rp), dimension(m), intent(inout) :: v
246 integer, dimension(m), intent(inout) :: dg
247 real(kind=rp), dimension(n), intent(inout) :: u
248 integer, dimension(m), intent(inout) :: gd
249 integer, dimension(nb), intent(inout) :: b
250 integer, dimension(nb), intent(inout) :: bo
251 integer, intent(in) :: o
252 integer, intent(in) :: op
253 logical, intent(in) :: shrd
254 integer :: i
255 type(c_ptr) :: u_d
256
257 u_d = device_get_ptr(u)
258
259 if (.not. shrd) then
260 associate(v_d => this%local_gs_d, dg_d => this%local_dof_gs_d, &
261 gd_d => this%local_gs_dof_d, b_d => this%local_blk_len_d, &
262 bo_d => this%local_blk_off_d, strm => this%gs_stream)
263
264 if (.not. c_associated(v_d)) then
265 call device_map(v, v_d, m)
266 block
267 real(c_rp) :: rp_dummy
268 integer(c_size_t) :: s
269 s = c_sizeof(rp_dummy) * m
270 call device_memset(v_d, 0, s, strm = strm)
271 end block
272 end if
273
274 if (.not. c_associated(dg_d)) then
275 call device_map(dg, dg_d, m)
276 call device_memcpy(dg, dg_d, m, host_to_device, &
277 sync = .false., strm = strm)
278 end if
279
280 if (.not. c_associated(gd_d)) then
281 call device_map(gd, gd_d, m)
282 call device_memcpy(gd, gd_d, m, host_to_device, &
283 sync = .false., strm = strm)
284 end if
285
286 if (nb .gt. 0) then
287 if (.not. c_associated(b_d)) then
288 call device_map(b, b_d, nb)
289 call device_memcpy(b, b_d, nb, host_to_device, &
290 sync = .false., strm = strm)
291 end if
292
293 if (.not. c_associated(bo_d)) then
294 call device_map(bo, bo_d, nb)
295 call device_memcpy(bo, bo_d, nb, host_to_device, &
296 sync = .false., strm = strm)
297 end if
298 end if
299
300#ifdef HAVE_HIP
301 call hip_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
302 nb, b_d, bo_d, op, strm)
303#elif HAVE_CUDA
304 call cuda_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
305 nb, b_d, bo_d, op, strm)
306#elif HAVE_OPENCL
307 call opencl_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
308 nb, b_d, bo_d, op, strm)
309#elif HAVE_METAL
310 call metal_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
311 nb, b_d, bo_d, op, strm)
312#else
313 call neko_error('No device backend configured')
314#endif
315
316 end associate
317 else if (shrd) then
318 associate(v_d => this%shared_gs_d, dg_d => this%shared_dof_gs_d, &
319 gd_d => this%shared_gs_dof_d, b_d => this%shared_blk_len_d, &
320 bo_d => this%shared_blk_off_d, strm => this%gs_stream)
321
322 if (.not. c_associated(v_d)) then
323 call device_map(v, v_d, m)
324 block
325 real(c_rp) :: rp_dummy
326 integer(c_size_t) :: s
327 s = c_sizeof(rp_dummy) * m
328 call device_memset(v_d, 0, s, strm = strm)
329 end block
330 end if
331
332 if (.not. c_associated(dg_d)) then
333 call device_map(dg, dg_d, m)
334 call device_memcpy(dg, dg_d, m, host_to_device, &
335 sync = .false., strm = strm)
336 end if
337
338 if (.not. c_associated(gd_d)) then
339 call device_map(gd, gd_d, m)
340 call device_memcpy(gd, gd_d, m, host_to_device, &
341 sync = .false., strm = strm)
342 end if
343
344 if (nb .gt. 0) then
345 if (.not. c_associated(b_d)) then
346 call device_map(b, b_d, nb)
347 call device_memcpy(b, b_d, nb, host_to_device, &
348 sync = .false., strm = strm)
349 end if
350
351 if (.not. c_associated(bo_d)) then
352 call device_map(bo, bo_d, nb)
353 call device_memcpy(bo, bo_d, nb, host_to_device, &
354 sync = .false., strm = strm)
355 end if
356 end if
357
358
359#ifdef HAVE_HIP
360 call hip_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
361 nb, b_d, bo_d, op, strm)
362#elif HAVE_CUDA
363 call cuda_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
364 nb, b_d, bo_d, op, strm)
365#elif HAVE_OPENCL
366 call opencl_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
367 nb, b_d, bo_d, op, strm)
368#elif HAVE_METAL
369 call metal_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
370 nb, b_d, bo_d, op, strm)
371#else
372 call neko_error('No device backend configured')
373#endif
374
375#if defined(HAVE_HIP) || defined(HAVE_CUDA) || \
376 defined(have_opencl) || defined(have_metal)
377 call device_event_record(this%gather_event, strm)
378#endif
379
380 if (this%shared_on_host) then
381 if (this%nshared .eq. m) then
382 call device_memcpy(v, v_d, m, device_to_host, &
383 sync = .true., strm = strm)
384 end if
385 end if
386
387 end associate
388 end if
389
390 end subroutine gs_gather_device
391
393 subroutine gs_scatter_device(this, v, m, dg, u, n, gd, nb, b, bo, shrd, event)
394 integer, intent(in) :: m
395 integer, intent(in) :: n
396 integer, intent(in) :: nb
397 class(gs_device_t), intent(inout) :: this
398 real(kind=rp), dimension(m), intent(inout) :: v
399 integer, dimension(m), intent(inout) :: dg
400 real(kind=rp), dimension(n), intent(inout) :: u
401 integer, dimension(m), intent(inout) :: gd
402 integer, dimension(nb), intent(inout) :: b
403 integer, dimension(nb), intent(inout) :: bo
404 logical, intent(in) :: shrd
405 type(c_ptr) :: event
406 type(c_ptr) :: u_d
407
408 u_d = device_get_ptr(u)
409
410 if (.not. shrd) then
411 associate(v_d => this%local_gs_d, dg_d => this%local_dof_gs_d, &
412 gd_d => this%local_gs_dof_d, b_d => this%local_blk_len_d, &
413 bo_d => this%local_blk_off_d, strm => this%gs_stream)
414#ifdef HAVE_HIP
415 call hip_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
416 strm)
417#elif HAVE_CUDA
418 call cuda_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
419 strm)
420#elif HAVE_OPENCL
421 call opencl_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
422 bo_d, strm)
423#elif HAVE_METAL
424 call metal_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
425 bo_d, strm)
426#else
427 call neko_error('No device backend configured')
428#endif
429 end associate
430 else if (shrd) then
431 associate(v_d => this%shared_gs_d, dg_d => this%shared_dof_gs_d, &
432 gd_d => this%shared_gs_dof_d, b_d => this%shared_blk_len_d, &
433 bo_d => this%shared_blk_off_d, strm => this%gs_stream)
434
435 if (this%shared_on_host) then
436 call device_memcpy(v, v_d, m, host_to_device, &
437 sync = .false., strm = strm)
438 end if
439
440#ifdef HAVE_HIP
441 call hip_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
442 strm)
443#elif HAVE_CUDA
444 call cuda_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
445 strm)
446#elif HAVE_OPENCL
447 call opencl_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
448 bo_d, strm)
449#elif HAVE_METAL
450 call metal_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
451 bo_d, strm)
452#else
453 call neko_error('No device backend configured')
454#endif
455 end associate
456 end if
457
458#if defined(HAVE_HIP) || defined(HAVE_CUDA) || \
459 defined(have_opencl) || defined(have_metal)
460 if (c_associated(event)) then
461 call device_event_record(event, this%gs_stream)
462 else
463 call device_sync(this%gs_stream)
464 end if
465#endif
466
467
468 end subroutine gs_scatter_device
469
470end module gs_device
void opencl_gather_kernel(void *v, int *m, int *o, void *dg, void *u, int *n, void *gd, int *nb, void *b, void *bo, int *op, void *cmd_queue)
Definition gs.c:58
void opencl_scatter_kernel(void *v, int *m, void *dg, void *u, int *n, void *gd, int *nb, void *b, void *bo, void *cmd_queue)
Definition gs.c:170
void cuda_gather_kernel(void *v, int *m, int *o, void *dg, void *u, int *n, void *gd, int *nb, void *b, void *bo, int *op, cudaStream_t stream)
Definition gs.cu:51
void cuda_scatter_kernel(void *v, int *m, void *dg, void *u, int *n, void *gd, int *nb, void *b, void *bo, cudaStream_t stream)
Definition gs.cu:96
Return the device pointer for an associated Fortran array.
Definition device.F90:108
Map a Fortran array to a device (allocate and associate)
Definition device.F90:78
Copy data between host and device (or device and device)
Definition device.F90:72
Synchronize a device or stream.
Definition device.F90:114
Device abstraction, common interface for various accelerators.
Definition device.F90:34
subroutine, public device_event_record(event, stream)
Record a device event.
Definition device.F90:1571
integer, parameter, public host_to_device
Definition device.F90:48
subroutine, public device_free(x_d)
Deallocate memory on the device.
Definition device.F90:240
integer, parameter, public device_to_host
Definition device.F90:48
subroutine, public device_event_destroy(event)
Destroy a device event.
Definition device.F90:1550
type(c_ptr), bind(C), public glb_cmd_queue
Global command queue.
Definition device.F90:52
subroutine, public device_event_create(event, flags)
Create a device event queue.
Definition device.F90:1516
subroutine, public device_memset(x_d, v, s, sync, strm)
Set memory on the device to a value.
Definition device.F90:263
Defines a gather-scatter backend.
Definition gs_bcknd.f90:34
Generic Gather-scatter backend for accelerators.
Definition gs_device.F90:34
subroutine gs_device_init(this, nlocal, nshared, nlcl_blks, nshrd_blks)
Accelerator backend initialisation.
subroutine gs_gather_device(this, v, m, o, dg, u, n, gd, nb, b, bo, op, shrd)
Gather kernel.
subroutine gs_device_free(this)
Dummy backend deallocation.
subroutine gs_scatter_device(this, v, m, dg, u, n, gd, nb, b, bo, shrd, event)
Scatter kernel.
integer, parameter, public c_rp
Definition num_types.f90:13
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Utilities.
Definition utils.f90:35
Gather-scatter backend.
Definition gs_bcknd.f90:44
Gather-scatter backend for offloading devices.
Definition gs_device.F90:48