Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_device_nccl.F90
Go to the documentation of this file.
1! Copyright (c) 2025, 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_comm, only : gs_comm_t, gs_vec_nc
37 use stack, only : stack_i4_t
38 use comm, only : pe_size, pe_rank
39 use htable, only : htable_i4_t
40 use device
41 use utils, only : neko_error
42 use, intrinsic :: iso_c_binding, only : c_sizeof, c_int32_t, &
43 c_ptr, c_null_ptr, c_size_t, c_associated, c_int
44 implicit none
45 private
46
48 type, private :: gs_device_nccl_buf_t
49 integer, allocatable :: ndofs(:)
50 integer, allocatable :: offset(:)
51 integer :: total
52 type(c_ptr) :: buf_d = c_null_ptr
53 type(c_ptr) :: buf_v_d = c_null_ptr
54 type(c_ptr) :: dof_d = c_null_ptr
55 contains
56 procedure, pass(this) :: init => gs_device_nccl_buf_init
57 procedure, pass(this) :: free => gs_device_nccl_buf_free
59
62 type, public, extends(gs_comm_t) :: gs_device_nccl_t
63 type(gs_device_nccl_buf_t) :: send_buf
64 type(gs_device_nccl_buf_t) :: recv_buf
65 type(c_ptr), allocatable :: stream(:)
66 type(c_ptr), allocatable :: event(:)
67 integer :: nb_strtgy
68 type(c_ptr) :: send_event = c_null_ptr
69 contains
70 procedure, pass(this) :: init => gs_device_nccl_init
71 procedure, pass(this) :: free => gs_device_nccl_free
72 procedure, pass(this) :: nbsend => gs_device_nccl_nbsend
73 procedure, pass(this) :: nbrecv => gs_device_nccl_nbrecv
74 procedure, pass(this) :: nbwait => gs_device_nccl_nbwait
75 procedure, pass(this) :: nbsend_vec => gs_device_nccl_nbsend_vec
76 procedure, pass(this) :: nbrecv_vec => gs_device_nccl_nbrecv_vec
77 procedure, pass(this) :: nbwait_vec => gs_device_nccl_nbwait_vec
78 end type gs_device_nccl_t
79
80#ifdef HAVE_HIP
81 interface
82 subroutine hip_gs_pack(u_d, buf_d, dof_d, offset, n, stream) &
83 bind(c, name = 'hip_gs_pack')
84 use, intrinsic :: iso_c_binding
85 implicit none
86 integer(c_int), value :: n, offset
87 type(c_ptr), value :: u_d, buf_d, dof_d, stream
88 end subroutine hip_gs_pack
89 end interface
90
91 interface
92 subroutine hip_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
93 bind(c, name = 'hip_gs_unpack')
94 use, intrinsic :: iso_c_binding
95 implicit none
96 integer(c_int), value :: op, offset, n
97 type(c_ptr), value :: u_d, buf_d, dof_d, stream
98 end subroutine hip_gs_unpack
99 end interface
100
101 interface
102 subroutine hip_gs_pack_vec(u_d, buf_d, dof_d, offset, n, nc, ns, stream) &
103 bind(c, name = 'hip_gs_pack_vec')
104 use, intrinsic :: iso_c_binding
105 implicit none
106 integer(c_int), value :: offset, n, nc, ns
107 type(c_ptr), value :: u_d, buf_d, dof_d, stream
108 end subroutine hip_gs_pack_vec
109 end interface
110
111 interface
112 subroutine hip_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
113 stream) bind(c, name = 'hip_gs_unpack_vec')
114 use, intrinsic :: iso_c_binding
115 implicit none
116 integer(c_int), value :: op, offset, n, nc, ns
117 type(c_ptr), value :: u_d, buf_d, dof_d, stream
118 end subroutine hip_gs_unpack_vec
119 end interface
120#elif HAVE_CUDA
121 interface
122 subroutine cuda_gs_pack(u_d, buf_d, dof_d, offset, n, stream) &
123 bind(c, name = 'cuda_gs_pack')
124 use, intrinsic :: iso_c_binding
125 implicit none
126 integer(c_int), value :: n, offset
127 type(c_ptr), value :: u_d, buf_d, dof_d, stream
128 end subroutine cuda_gs_pack
129 end interface
130
131 interface
132 subroutine cuda_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
133 bind(c, name = 'cuda_gs_unpack')
134 use, intrinsic :: iso_c_binding
135 implicit none
136 integer(c_int), value :: op, offset, n
137 type(c_ptr), value :: u_d, buf_d, dof_d, stream
138 end subroutine cuda_gs_unpack
139 end interface
140
141 interface
142 subroutine cuda_gs_pack_vec(u_d, buf_d, dof_d, offset, n, nc, ns, stream) &
143 bind(c, name = 'cuda_gs_pack_vec')
144 use, intrinsic :: iso_c_binding
145 implicit none
146 integer(c_int), value :: offset, n, nc, ns
147 type(c_ptr), value :: u_d, buf_d, dof_d, stream
148 end subroutine cuda_gs_pack_vec
149 end interface
150
151 interface
152 subroutine cuda_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
153 stream) bind(c, name = 'cuda_gs_unpack_vec')
154 use, intrinsic :: iso_c_binding
155 implicit none
156 integer(c_int), value :: op, offset, n, nc, ns
157 type(c_ptr), value :: u_d, buf_d, dof_d, stream
158 end subroutine cuda_gs_unpack_vec
159 end interface
160#endif
161
162 interface
163 subroutine device_nccl_sendrecv(sbuf_d, soffset, scount, srank, &
164 rbuf_d, roffset, rcount, rrank, nbytes, stream) &
165 bind(c, name = 'device_nccl_sendrecv')
166 use, intrinsic :: iso_c_binding
167 implicit none
168 integer(c_int), value :: soffset, scount, roffset, rcount
169 integer(c_int), value :: srank, rrank, nbytes
170 type(c_ptr), value :: sbuf_d, rbuf_d, stream
171 end subroutine device_nccl_sendrecv
172 end interface
173
174contains
175
176 subroutine gs_device_nccl_buf_init(this, pe_order, dof_stack, mark_dupes)
177 class(gs_device_nccl_buf_t), intent(inout) :: this
178 integer, allocatable, intent(inout) :: pe_order(:)
179 type(stack_i4_t), allocatable, intent(inout) :: dof_stack(:)
180 logical, intent(in) :: mark_dupes
181 integer, allocatable :: dofs(:)
182 integer :: i, j, total
183 integer(c_size_t) :: sz
184 type(htable_i4_t) :: doftable
185 integer :: dupe, marked, k
186 real(c_rp) :: rp_dummy
187 integer(c_int32_t) :: i4_dummy
188
189
190 allocate(this%ndofs(size(pe_order)))
191 allocate(this%offset(size(pe_order)))
192
193 total = 0
194 do i = 1, size(pe_order)
195 this%ndofs(i) = dof_stack(pe_order(i))%size()
196 this%offset(i) = total
197 total = total + this%ndofs(i)
198 end do
199
200 this%total = total
201
202 sz = c_sizeof(rp_dummy) * total
203 call device_alloc(this%buf_d, sz)
204
205 ! Fused vector buffer, sized for up to GS_VEC_NC components.
206 sz = c_sizeof(rp_dummy) * gs_vec_nc * total
207 call device_alloc(this%buf_v_d, sz)
208
209 sz = c_sizeof(i4_dummy) * total
210 call device_alloc(this%dof_d, sz)
211
212 if (mark_dupes) call doftable%init(2*total)
213 allocate(dofs(total))
214
215 ! Copy from dof_stack into dofs, optionally marking duplicates with doftable
216 marked = 0
217 do i = 1, size(pe_order)
218 ! %array() breaks on cray
219 select type (arr => dof_stack(pe_order(i))%data)
220 type is (integer)
221 do j = 1, this%ndofs(i)
222 k = this%offset(i) + j
223 if (mark_dupes) then
224 if (doftable%get(arr(j), dupe) .eq. 0) then
225 if (dofs(dupe) .gt. 0) then
226 dofs(dupe) = -dofs(dupe)
227 marked = marked + 1
228 end if
229 dofs(k) = -arr(j)
230 marked = marked + 1
231 else
232 call doftable%set(arr(j), k)
233 dofs(k) = arr(j)
234 end if
235 else
236 dofs(k) = arr(j)
237 end if
238 end do
239 end select
240 end do
241
242 call device_memcpy(dofs, this%dof_d, total, host_to_device, sync = .true.)
243
244 deallocate(dofs)
245 call doftable%free()
246
247 end subroutine gs_device_nccl_buf_init
248
249 subroutine gs_device_nccl_buf_free(this)
250 class(gs_device_nccl_buf_t), intent(inout) :: this
251
252 if (allocated(this%ndofs)) deallocate(this%ndofs)
253 if (allocated(this%offset)) deallocate(this%offset)
254
255 if (c_associated(this%buf_d)) call device_free(this%buf_d)
256 if (c_associated(this%buf_v_d)) call device_free(this%buf_v_d)
257 if (c_associated(this%dof_d)) call device_free(this%dof_d)
258 end subroutine gs_device_nccl_buf_free
259
261 subroutine gs_device_nccl_init(this, send_pe, recv_pe)
262 class(gs_device_nccl_t), intent(inout) :: this
263 type(stack_i4_t), intent(inout) :: send_pe
264 type(stack_i4_t), intent(inout) :: recv_pe
265 integer :: i, nstrm
266
267 call this%init_order(send_pe, recv_pe)
268
269 call this%send_buf%init(this%send_pe, this%send_dof, .false.)
270 call this%recv_buf%init(this%recv_pe, this%recv_dof, .true.)
271
272#if defined(HAVE_HIP) || defined(HAVE_CUDA)
273 ! Create a set of non-blocking streams. The per-peer streams and events
274 ! are indexed over both send_pe (pack, sendrecv) and recv_pe (unpack,
275 ! sync), so size them for the larger of the two peer lists.
276 nstrm = max(size(this%send_pe), size(this%recv_pe))
277 allocate(this%stream(nstrm))
278 do i = 1, nstrm
279 call device_stream_create_with_priority(this%stream(i), 1, &
281 end do
282
283 allocate(this%event(nstrm))
284 do i = 1, nstrm
285 call device_event_create(this%event(i), 2)
286 end do
287#endif
288
289 this%vec_supported = .true.
290
291 end subroutine gs_device_nccl_init
292
294 subroutine gs_device_nccl_free(this)
295 class(gs_device_nccl_t), intent(inout) :: this
296 integer :: i
297
298 call this%send_buf%free()
299 call this%recv_buf%free()
300
301 call this%free_order()
302 call this%free_dofs()
303
304#if defined(HAVE_HIP) || defined(HAVE_CUDA)
305 if (allocated(this%stream)) then
306 do i = 1, size(this%stream)
307 call device_stream_destroy(this%stream(i))
308 end do
309 deallocate(this%stream)
310 end if
311#endif
312
313 end subroutine gs_device_nccl_free
314
316 subroutine gs_device_nccl_nbsend(this, u, n, tag, deps, strm)
317 class(gs_device_nccl_t), intent(inout) :: this
318 integer, intent(in) :: n
319 real(kind=rp), dimension(n), intent(inout) :: u
320 integer, intent(in) :: tag
321 type(c_ptr), intent(inout) :: deps
322 type(c_ptr), intent(inout) :: strm
323 integer :: i
324 type(c_ptr) :: u_d
325
326 u_d = device_get_ptr(u)
327
328 do i = 1, size(this%send_pe)
329 call device_stream_wait_event(this%stream(i), deps, 0)
330#ifdef HAVE_HIP
331 call hip_gs_pack(u_d, &
332 this%send_buf%buf_d, &
333 this%send_buf%dof_d, &
334 this%send_buf%offset(i), &
335 this%send_buf%ndofs(i), &
336 this%stream(i))
337#elif HAVE_CUDA
338 call cuda_gs_pack(u_d, &
339 this%send_buf%buf_d, &
340 this%send_buf%dof_d, &
341 this%send_buf%offset(i), &
342 this%send_buf%ndofs(i), &
343 this%stream(i))
344#else
345 call neko_error('gs_device_nccl: no backend')
346#endif
347 end do
348
349
350 ! Everything else is done in the wait routine
351
352 end subroutine gs_device_nccl_nbsend
353
355 subroutine gs_device_nccl_nbrecv(this, tag)
356 class(gs_device_nccl_t), intent(inout) :: this
357 integer, intent(in) :: tag
358 integer :: i
359
360 ! Everything is done in the wait routine
361
362 end subroutine gs_device_nccl_nbrecv
363
365 subroutine gs_device_nccl_nbwait(this, u, n, op, strm)
366 class(gs_device_nccl_t), intent(inout) :: this
367 integer, intent(in) :: n
368 real(kind=rp), dimension(n), intent(inout) :: u
369 type(c_ptr), intent(inout) :: strm
370 integer :: op, done_req, i
371 type(c_ptr) :: u_d
372 real(c_rp) :: rp_dummy
373 integer(c_int) :: nbytes
374
375 u_d = device_get_ptr(u)
376 nbytes = c_sizeof(rp_dummy)
377
378 do i = 1, size(this%send_pe)
379
380 call device_nccl_sendrecv(this%send_buf%buf_d, &
381 nbytes*this%send_buf%offset(i), &
382 this%send_buf%ndofs(i), &
383 this%send_pe(i), &
384 this%recv_buf%buf_d, &
385 nbytes*this%recv_buf%offset(i), &
386 this%recv_buf%ndofs(i), &
387 this%recv_pe(i), &
388 nbytes, &
389 this%stream(i))
390
391#ifdef HAVE_HIP
392 call hip_gs_unpack(u_d, op, &
393 this%recv_buf%buf_d, &
394 this%recv_buf%dof_d, &
395 this%recv_buf%offset(i), &
396 this%recv_buf%ndofs(i), &
397 this%stream(i))
398#elif HAVE_CUDA
399 call cuda_gs_unpack(u_d, op, &
400 this%recv_buf%buf_d, &
401 this%recv_buf%dof_d, &
402 this%recv_buf%offset(i), &
403 this%recv_buf%ndofs(i), &
404 this%stream(i))
405#else
406 call neko_error('gs_device_mpi: no backend')
407#endif
408 call device_event_record(this%event(i), this%stream(i))
409 end do
410
411 ! Sync non-blocking streams
412 do done_req = 1, size(this%recv_pe)
413 call device_stream_wait_event(strm, &
414 this%event(done_req), 0)
415 end do
416
417 end subroutine gs_device_nccl_nbwait
418
422 subroutine gs_device_nccl_nbsend_vec(this, u, n, nc, tag, deps, strm)
423 class(gs_device_nccl_t), intent(inout) :: this
424 integer, intent(in) :: n, nc
425 real(kind=rp), dimension(nc*n), intent(inout) :: u
426 integer, intent(in) :: tag
427 type(c_ptr), intent(inout) :: deps
428 type(c_ptr), intent(inout) :: strm
429 integer :: i
430 type(c_ptr) :: u_d
431
432 u_d = device_get_ptr(u)
433
434 do i = 1, size(this%send_pe)
435 call device_stream_wait_event(this%stream(i), deps, 0)
436#ifdef HAVE_HIP
437 call hip_gs_pack_vec(u_d, this%send_buf%buf_v_d, this%send_buf%dof_d, &
438 this%send_buf%offset(i), this%send_buf%ndofs(i), nc, n, &
439 this%stream(i))
440#elif HAVE_CUDA
441 call cuda_gs_pack_vec(u_d, this%send_buf%buf_v_d, this%send_buf%dof_d, &
442 this%send_buf%offset(i), this%send_buf%ndofs(i), nc, n, &
443 this%stream(i))
444#else
445 call neko_error('gs_device_nccl: no backend')
446#endif
447 end do
448
449 end subroutine gs_device_nccl_nbsend_vec
450
452 subroutine gs_device_nccl_nbrecv_vec(this, tag, nc)
453 class(gs_device_nccl_t), intent(inout) :: this
454 integer, intent(in) :: tag, nc
455 end subroutine gs_device_nccl_nbrecv_vec
456
458 subroutine gs_device_nccl_nbwait_vec(this, u, n, nc, op, strm)
459 class(gs_device_nccl_t), intent(inout) :: this
460 integer, intent(in) :: n, nc
461 real(kind=rp), dimension(nc*n), intent(inout) :: u
462 type(c_ptr), intent(inout) :: strm
463 integer :: op, done_req, i
464 type(c_ptr) :: u_d
465 real(c_rp) :: rp_dummy
466 integer(c_int) :: nbytes
467
468 u_d = device_get_ptr(u)
469 nbytes = c_sizeof(rp_dummy)
470
471 do i = 1, size(this%send_pe)
472
473 call device_nccl_sendrecv(this%send_buf%buf_v_d, &
474 nbytes*nc*this%send_buf%offset(i), &
475 nc*this%send_buf%ndofs(i), &
476 this%send_pe(i), &
477 this%recv_buf%buf_v_d, &
478 nbytes*nc*this%recv_buf%offset(i), &
479 nc*this%recv_buf%ndofs(i), &
480 this%recv_pe(i), &
481 nbytes, &
482 this%stream(i))
483
484#ifdef HAVE_HIP
485 call hip_gs_unpack_vec(u_d, op, this%recv_buf%buf_v_d, &
486 this%recv_buf%dof_d, this%recv_buf%offset(i), &
487 this%recv_buf%ndofs(i), nc, n, this%stream(i))
488#elif HAVE_CUDA
489 call cuda_gs_unpack_vec(u_d, op, this%recv_buf%buf_v_d, &
490 this%recv_buf%dof_d, this%recv_buf%offset(i), &
491 this%recv_buf%ndofs(i), nc, n, this%stream(i))
492#else
493 call neko_error('gs_device_nccl: no backend')
494#endif
495 call device_event_record(this%event(i), this%stream(i))
496 end do
497
498 ! Sync non-blocking streams
499 do done_req = 1, size(this%recv_pe)
500 call device_stream_wait_event(strm, &
501 this%event(done_req), 0)
502 end do
503
504 end subroutine gs_device_nccl_nbwait_vec
505
506end module gs_device_nccl
void cuda_gs_unpack(real *u_d, int op, real *buf_d, int *dof_d, int offset, int n, cudaStream_t stream)
Definition gs.cu:132
void cuda_gs_pack(void *u_d, void *buf_d, void *dof_d, int offset, int n, cudaStream_t stream)
Definition gs.cu:116
void cuda_gs_pack_vec(void *u_d, void *buf_d, void *dof_d, int offset, int n, int nc, int ns, cudaStream_t stream)
Definition gs.cu:166
void cuda_gs_unpack_vec(real *u_d, int op, real *buf_d, int *dof_d, int offset, int n, int nc, int ns, cudaStream_t stream)
Definition gs.cu:183
Return the device pointer for an associated Fortran array.
Definition device.F90:108
Copy data between host and device (or device and device)
Definition device.F90:72
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:61
integer, public pe_rank
MPI rank.
Definition comm.F90:58
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
subroutine, public device_alloc(x_d, s)
Allocate memory on the device.
Definition device.F90:209
subroutine, public device_stream_create_with_priority(stream, flags, prio)
Create a device stream/command queue with priority.
Definition device.F90:1427
subroutine, public device_stream_wait_event(stream, event, flags)
Synchronize a device stream with an event.
Definition device.F90:1471
subroutine, public device_event_create(event, flags)
Create a device event queue.
Definition device.F90:1516
integer, public strm_high_prio
High priority stream setting.
Definition device.F90:66
subroutine, public device_stream_destroy(stream)
Destroy a device stream/command queue.
Definition device.F90:1449
Defines a gather-scatter communication method.
Definition gs_comm.f90:34
integer, parameter, public gs_vec_nc
Maximum number of components handled by the fused vector (multi-component) halo exchange used by gs_o...
Definition gs_comm.f90:49
Defines NCCL based gather-scatter communication.
subroutine gs_device_nccl_nbsend_vec(this, u, n, nc, tag, deps, strm)
Fused nc-component send. u is the compact shared device buffer (component-outer, per-component stride...
subroutine gs_device_nccl_nbrecv(this, tag)
Post non-blocking receive operations.
subroutine gs_device_nccl_init(this, send_pe, recv_pe)
Initialise NCCL based communication method.
subroutine gs_device_nccl_nbsend(this, u, n, tag, deps, strm)
Post non-blocking send operations.
subroutine gs_device_nccl_nbwait(this, u, n, op, strm)
Wait for non-blocking operations.
subroutine gs_device_nccl_buf_init(this, pe_order, dof_stack, mark_dupes)
subroutine gs_device_nccl_nbrecv_vec(this, tag, nc)
No-op: send/recv and unpack happen in nbwait_vec.
subroutine gs_device_nccl_nbwait_vec(this, u, n, nc, op, strm)
Fused nc-component send/recv + unpack.
subroutine gs_device_nccl_free(this)
Deallocate MPI based communication method.
subroutine gs_device_nccl_buf_free(this)
Implements a hash table ADT.
Definition htable.f90:52
integer, parameter, public c_rp
Definition num_types.f90:13
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:52
Buffers for non-blocking communication and packing/unpacking.
Gather-scatter communication using NCCL The arrays are indexed per PE like send_pe and @ recv_pe.
Integer based hash table.
Definition htable.f90:102
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40