Neko 1.99.6
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%shared_gs_d)) then
205 call device_free(this%shared_gs_d)
206 end if
207
208 if (c_associated(this%shared_dof_gs_d)) then
209 call device_free(this%shared_dof_gs_d)
210 end if
211
212 if (c_associated(this%shared_gs_dof_d)) then
213 call device_free(this%shared_gs_dof_d)
214 end if
215
216 if (c_associated(this%local_blk_len_d)) then
217 call device_free(this%local_blk_len_d)
218 end if
219
220 if (c_associated(this%shared_blk_len_d)) then
221 call device_free(this%shared_blk_len_d)
222 end if
223
224 if (c_associated(this%local_blk_off_d)) then
225 call device_free(this%local_blk_off_d)
226 end if
227
228 if (c_associated(this%shared_blk_off_d)) then
229 call device_free(this%shared_blk_off_d)
230 end if
231
232 this%nlocal = 0
233 this%nshared = 0
234
235#if defined(HAVE_HIP) || defined(HAVE_CUDA) || defined(HAVE_METAL)
236 if (c_associated(this%gather_event)) then
237 call device_event_destroy(this%gather_event)
238 end if
239
240 if (c_associated(this%scatter_event)) then
241 call device_event_destroy(this%scatter_event)
242 end if
243#endif
244
245 if (c_associated(this%gs_stream)) then
246 this%gs_stream = c_null_ptr
247 end if
248
249 end subroutine gs_device_free
250
252 subroutine gs_gather_device(this, v, m, o, dg, u, n, gd, nb, b, bo, op, shrd)
253 integer, intent(in) :: m
254 integer, intent(in) :: n
255 integer, intent(in) :: nb
256 class(gs_device_t), intent(inout) :: this
257 real(kind=rp), dimension(m), intent(inout) :: v
258 integer, dimension(m), intent(inout) :: dg
259 real(kind=rp), dimension(n), intent(inout) :: u
260 integer, dimension(m), intent(inout) :: gd
261 integer, dimension(nb), intent(inout) :: b
262 integer, dimension(nb), intent(inout) :: bo
263 integer, intent(in) :: o
264 integer, intent(in) :: op
265 logical, intent(in) :: shrd
266 integer :: i
267 type(c_ptr) :: u_d
268
269 u_d = device_get_ptr(u)
270
271 if (.not. shrd) then
272 associate(v_d => this%local_gs_d, dg_d => this%local_dof_gs_d, &
273 gd_d => this%local_gs_dof_d, b_d => this%local_blk_len_d, &
274 bo_d => this%local_blk_off_d, strm => this%gs_stream)
275
276 if (.not. c_associated(v_d)) then
277 call device_map(v, v_d, m)
278 block
279 real(c_rp) :: rp_dummy
280 integer(c_size_t) :: s
281 s = c_sizeof(rp_dummy) * m
282 call device_memset(v_d, 0, s, strm = strm)
283 end block
284 end if
285
286 if (.not. c_associated(dg_d)) then
287 call device_map(dg, dg_d, m)
288 call device_memcpy(dg, dg_d, m, host_to_device, &
289 sync = .false., strm = strm)
290 end if
291
292 if (.not. c_associated(gd_d)) then
293 call device_map(gd, gd_d, m)
294 call device_memcpy(gd, gd_d, m, host_to_device, &
295 sync = .false., strm = strm)
296 end if
297
298 if (nb .gt. 0) then
299 if (.not. c_associated(b_d)) then
300 call device_map(b, b_d, nb)
301 call device_memcpy(b, b_d, nb, host_to_device, &
302 sync = .false., strm = strm)
303 end if
304
305 if (.not. c_associated(bo_d)) then
306 call device_map(bo, bo_d, nb)
307 call device_memcpy(bo, bo_d, nb, host_to_device, &
308 sync = .false., strm = strm)
309 end if
310 end if
311
312#ifdef HAVE_HIP
313 call hip_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
314 nb, b_d, bo_d, op, strm)
315#elif HAVE_CUDA
316 call cuda_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
317 nb, b_d, bo_d, op, strm)
318#elif HAVE_OPENCL
319 call opencl_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
320 nb, b_d, bo_d, op, strm)
321#elif HAVE_METAL
322 call metal_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
323 nb, b_d, bo_d, op, strm)
324#else
325 call neko_error('No device backend configured')
326#endif
327
328 end associate
329 else if (shrd) then
330 associate(v_d => this%shared_gs_d, dg_d => this%shared_dof_gs_d, &
331 gd_d => this%shared_gs_dof_d, b_d => this%shared_blk_len_d, &
332 bo_d => this%shared_blk_off_d, strm => this%gs_stream)
333
334 if (.not. c_associated(v_d)) then
335 call device_map(v, v_d, m)
336 block
337 real(c_rp) :: rp_dummy
338 integer(c_size_t) :: s
339 s = c_sizeof(rp_dummy) * m
340 call device_memset(v_d, 0, s, strm = strm)
341 end block
342 end if
343
344 if (.not. c_associated(dg_d)) then
345 call device_map(dg, dg_d, m)
346 call device_memcpy(dg, dg_d, m, host_to_device, &
347 sync = .false., strm = strm)
348 end if
349
350 if (.not. c_associated(gd_d)) then
351 call device_map(gd, gd_d, m)
352 call device_memcpy(gd, gd_d, m, host_to_device, &
353 sync = .false., strm = strm)
354 end if
355
356 if (nb .gt. 0) then
357 if (.not. c_associated(b_d)) then
358 call device_map(b, b_d, nb)
359 call device_memcpy(b, b_d, nb, host_to_device, &
360 sync = .false., strm = strm)
361 end if
362
363 if (.not. c_associated(bo_d)) then
364 call device_map(bo, bo_d, nb)
365 call device_memcpy(bo, bo_d, nb, host_to_device, &
366 sync = .false., strm = strm)
367 end if
368 end if
369
370
371#ifdef HAVE_HIP
372 call hip_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
373 nb, b_d, bo_d, op, strm)
374#elif HAVE_CUDA
375 call cuda_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
376 nb, b_d, bo_d, op, strm)
377#elif HAVE_OPENCL
378 call opencl_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
379 nb, b_d, bo_d, op, strm)
380#elif HAVE_METAL
381 call metal_gather_kernel(v_d, m, o, dg_d, u_d, n, gd_d, &
382 nb, b_d, bo_d, op, strm)
383#else
384 call neko_error('No device backend configured')
385#endif
386
387#if defined(HAVE_HIP) || defined(HAVE_CUDA) || \
388 defined(have_opencl) || defined(have_metal)
389 call device_event_record(this%gather_event, strm)
390#endif
391
392 if (this%shared_on_host) then
393 if (this%nshared .eq. m) then
394 call device_memcpy(v, v_d, m, device_to_host, &
395 sync = .true., strm = strm)
396 end if
397 end if
398
399 end associate
400 end if
401
402 end subroutine gs_gather_device
403
405 subroutine gs_scatter_device(this, v, m, dg, u, n, gd, nb, b, bo, shrd, event)
406 integer, intent(in) :: m
407 integer, intent(in) :: n
408 integer, intent(in) :: nb
409 class(gs_device_t), intent(inout) :: this
410 real(kind=rp), dimension(m), intent(inout) :: v
411 integer, dimension(m), intent(inout) :: dg
412 real(kind=rp), dimension(n), intent(inout) :: u
413 integer, dimension(m), intent(inout) :: gd
414 integer, dimension(nb), intent(inout) :: b
415 integer, dimension(nb), intent(inout) :: bo
416 logical, intent(in) :: shrd
417 type(c_ptr) :: event
418 type(c_ptr) :: u_d
419
420 u_d = device_get_ptr(u)
421
422 if (.not. shrd) then
423 associate(v_d => this%local_gs_d, dg_d => this%local_dof_gs_d, &
424 gd_d => this%local_gs_dof_d, b_d => this%local_blk_len_d, &
425 bo_d => this%local_blk_off_d, strm => this%gs_stream)
426#ifdef HAVE_HIP
427 call hip_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
428 strm)
429#elif HAVE_CUDA
430 call cuda_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
431 strm)
432#elif HAVE_OPENCL
433 call opencl_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
434 bo_d, strm)
435#elif HAVE_METAL
436 call metal_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
437 bo_d, strm)
438#else
439 call neko_error('No device backend configured')
440#endif
441 end associate
442 else if (shrd) then
443 associate(v_d => this%shared_gs_d, dg_d => this%shared_dof_gs_d, &
444 gd_d => this%shared_gs_dof_d, b_d => this%shared_blk_len_d, &
445 bo_d => this%shared_blk_off_d, strm => this%gs_stream)
446
447 if (this%shared_on_host) then
448 call device_memcpy(v, v_d, m, host_to_device, &
449 sync = .false., strm = strm)
450 end if
451
452#ifdef HAVE_HIP
453 call hip_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
454 strm)
455#elif HAVE_CUDA
456 call cuda_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, bo_d, &
457 strm)
458#elif HAVE_OPENCL
459 call opencl_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
460 bo_d, strm)
461#elif HAVE_METAL
462 call metal_scatter_kernel(v_d, m, dg_d, u_d, n, gd_d, nb, b_d, &
463 bo_d, strm)
464#else
465 call neko_error('No device backend configured')
466#endif
467 end associate
468 end if
469
470#if defined(HAVE_HIP) || defined(HAVE_CUDA) || \
471 defined(have_opencl) || defined(have_metal)
472 if (c_associated(event)) then
473 call device_event_record(event, this%gs_stream)
474 else
475 call device_sync(this%gs_stream)
476 end if
477#endif
478
479
480 end subroutine gs_scatter_device
481
482end 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:113
Map a Fortran array to a device (allocate and associate)
Definition device.F90:83
Copy data between host and device (or device and device)
Definition device.F90:72
Synchronize a device or stream.
Definition device.F90:119
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:1644
integer, parameter, public host_to_device
Definition device.F90:48
subroutine, public device_free(x_d)
Deallocate memory on the device.
Definition device.F90:243
integer, parameter, public device_to_host
Definition device.F90:48
subroutine, public device_event_destroy(event)
Destroy a device event.
Definition device.F90:1623
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:1589
subroutine, public device_memset(x_d, v, s, sync, strm)
Set memory on the device to a value.
Definition device.F90:268
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