Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_device_shmem.F90
Go to the documentation of this file.
1! Copyright (c) 2020-2024, 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 htable, only : htable_i4_t
43 use comm, only : pe_size, pe_rank, neko_comm
44 use mpi_f08, only : mpi_allreduce, mpi_alltoall, mpi_integer, mpi_max
45 use utils, only : neko_error
46 use, intrinsic :: iso_c_binding, only : c_sizeof, c_int32_t, c_int64_t, &
47 c_ptr, c_null_ptr, c_size_t, c_associated
48 implicit none
49 private
50
54#if defined(HAVE_CUDA) && defined(HAVE_NVSHMEM)
55 logical, parameter, public :: GS_DEVICE_SHMEM_AVAIL = .true.
56#else
57 logical, parameter, public :: gs_device_shmem_avail = .false.
58#endif
59
61 type, private :: gs_device_shmem_buf_t
62 integer, allocatable :: ndofs(:)
63 integer, allocatable :: offset(:)
64 integer, allocatable :: remote_offset(:)
65 integer :: total
69 integer :: slab_stride
70 type(c_ptr) :: buf_d = c_null_ptr
71 type(c_ptr) :: buf_v_d = c_null_ptr
72 type(c_ptr) :: dof_d = c_null_ptr
73 contains
74 procedure, pass(this) :: init => gs_device_shmem_buf_init
75 procedure, pass(this) :: free => gs_device_shmem_buf_free
77
80 type, public, extends(gs_comm_t) :: gs_device_shmem_t
81 type(gs_device_shmem_buf_t) :: send_buf
82 type(gs_device_shmem_buf_t) :: recv_buf
83 type(c_ptr), allocatable :: stream(:)
84 type(c_ptr), allocatable :: event(:)
89 integer :: iter = 0
96 type(c_ptr) :: done_sig_d = c_null_ptr
97 type(c_ptr) :: ready_sig_d = c_null_ptr
101 type(c_ptr) :: pack_event = c_null_ptr
102 contains
103 procedure, pass(this) :: init => gs_device_shmem_init
104 procedure, pass(this) :: free => gs_device_shmem_free
105 procedure, pass(this) :: nbsend => gs_device_shmem_nbsend
106 procedure, pass(this) :: nbrecv => gs_device_shmem_nbrecv
107 procedure, pass(this) :: nbwait => gs_device_shmem_nbwait
108 procedure, pass(this) :: nbsend_vec => gs_device_shmem_nbsend_vec
109 procedure, pass(this) :: nbrecv_vec => gs_device_shmem_nbrecv_vec
110 procedure, pass(this) :: nbwait_vec => gs_device_shmem_nbwait_vec
111 end type gs_device_shmem_t
112
113
114#if defined (HAVE_CUDA) && defined(HAVE_NVSHMEM)
115
116 interface
117 subroutine cudamalloc_nvshmem(ptr, size) &
118 bind(c, name = 'cudamalloc_nvshmem')
119 use, intrinsic :: iso_c_binding
120 implicit none
121 type(c_ptr) :: ptr
122 integer(c_size_t), value :: size
123 end subroutine cudamalloc_nvshmem
124 end interface
125
126 interface
127 subroutine cudafree_nvshmem(ptr) &
128 bind(c, name = 'cudafree_nvshmem')
129 use, intrinsic :: iso_c_binding
130 implicit none
131 type(c_ptr) :: ptr
132 end subroutine cudafree_nvshmem
133 end interface
134
135 interface
136 subroutine cuda_gs_nvshmem_pack(u_d, buf_d, dof_d, boffset, n, stream) &
137 bind(c, name = 'cuda_gs_nvshmem_pack')
138 use, intrinsic :: iso_c_binding
139 implicit none
140 integer(c_int), value :: boffset, n
141 type(c_ptr), value :: u_d, buf_d, dof_d, stream
142 end subroutine cuda_gs_nvshmem_pack
143 end interface
144
145 interface
146 subroutine cuda_gs_nvshmem_pack_vec(u_d, buf_d, dof_d, boffset, n, nc, &
147 ns, stream) bind(c, name = 'cuda_gs_nvshmem_pack_vec')
148 use, intrinsic :: iso_c_binding
149 implicit none
150 integer(c_int), value :: boffset, n, nc, ns
151 type(c_ptr), value :: u_d, buf_d, dof_d, stream
152 end subroutine cuda_gs_nvshmem_pack_vec
153 end interface
154
155 interface
156 subroutine cuda_gs_push(buf_d, offset, n, stream, dest_rank, rbuf_d, &
157 roffset, iter, done_d, ready_d, mype) &
158 bind(c, name = 'cuda_gs_push')
159 use, intrinsic :: iso_c_binding
160 implicit none
161 integer(c_int), value :: n, offset, dest_rank, roffset, iter, mype
162 type(c_ptr), value :: buf_d, stream, rbuf_d, done_d, ready_d
163 end subroutine cuda_gs_push
164 end interface
165
166 interface
167 subroutine cuda_gs_push_wait(stream, iter, done_d, src_rank) &
168 bind(c, name = 'cuda_gs_push_wait')
169 use, intrinsic :: iso_c_binding
170 implicit none
171 integer(c_int), value :: iter, src_rank
172 type(c_ptr), value :: stream, done_d
173 end subroutine cuda_gs_push_wait
174 end interface
175
176 interface
177 subroutine cuda_gs_post_ready(stream, iter, ready_d, mype, src_rank) &
178 bind(c, name = 'cuda_gs_post_ready')
179 use, intrinsic :: iso_c_binding
180 implicit none
181 integer(c_int), value :: iter, mype, src_rank
182 type(c_ptr), value :: stream, ready_d
183 end subroutine cuda_gs_post_ready
184 end interface
185
186 interface
187 subroutine cuda_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
188 bind(c, name = 'cuda_gs_unpack')
189 use, intrinsic :: iso_c_binding
190 implicit none
191 integer(c_int), value :: op, offset, n
192 type(c_ptr), value :: u_d, buf_d, dof_d, stream
193 end subroutine cuda_gs_unpack
194 end interface
195
196 interface
197 subroutine cuda_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
198 stream) bind(c, name = 'cuda_gs_unpack_vec')
199 use, intrinsic :: iso_c_binding
200 implicit none
201 integer(c_int), value :: op, offset, n, nc, ns
202 type(c_ptr), value :: u_d, buf_d, dof_d, stream
203 end subroutine cuda_gs_unpack_vec
204 end interface
205#endif
206
207contains
208
213 subroutine gs_device_shmem_buf_init(this, pe_order, dof_stack, mark_dupes, &
214 nslabs)
215 class(gs_device_shmem_buf_t), intent(inout) :: this
216 integer, allocatable, intent(inout) :: pe_order(:)
217 type(stack_i4_t), allocatable, intent(inout) :: dof_stack(:)
218 logical, intent(in) :: mark_dupes
219 integer, intent(in) :: nslabs
220 integer, allocatable :: dofs(:)
221 integer :: i, j, total, max_total
222 integer(c_size_t) :: sz
223 type(htable_i4_t) :: doftable
224 integer :: dupe, marked, k
225 real(c_rp) :: rp_dummy
226 integer(c_int32_t) :: i4_dummy
227
228 allocate(this%ndofs(size(pe_order)))
229 allocate(this%offset(size(pe_order)))
230 allocate(this%remote_offset(size(pe_order)))
231
232 do i = 1, size(pe_order)
233 this%remote_offset(i) = -1
234 end do
235
236 total = 0
237 do i = 1, size(pe_order)
238 this%ndofs(i) = dof_stack(pe_order(i))%size()
239 this%offset(i) = total
240 total = total + this%ndofs(i)
241 end do
242
243 call mpi_allreduce(total, max_total, 1, mpi_integer, mpi_max, neko_comm)
244
245 this%total = total
246 this%slab_stride = max_total
247
248 sz = c_sizeof(rp_dummy) * nslabs * max_total
249#ifdef HAVE_NVSHMEM
250 call cudamalloc_nvshmem(this%buf_d, sz)
251 ! Fused vector symmetric buffer, sized for up to GS_VEC_NC components.
252 sz = c_sizeof(rp_dummy) * nslabs * gs_vec_nc * max_total
253 call cudamalloc_nvshmem(this%buf_v_d, sz)
254#endif
255
256 sz = c_sizeof(i4_dummy) * total
257 call device_alloc(this%dof_d, sz)
258
259 if (mark_dupes) call doftable%init(2*total)
260 allocate(dofs(total))
261
262 ! Copy from dof_stack into dofs, optionally marking duplicates with doftable
263 marked = 0
264 do i = 1, size(pe_order)
265 ! %array() breaks on cray
266 select type (arr => dof_stack(pe_order(i))%data)
267 type is (integer)
268 do j = 1, this%ndofs(i)
269 k = this%offset(i) + j
270 if (mark_dupes) then
271 if (doftable%get(arr(j), dupe) .eq. 0) then
272 if (dofs(dupe) .gt. 0) then
273 dofs(dupe) = -dofs(dupe)
274 marked = marked + 1
275 end if
276 dofs(k) = -arr(j)
277 marked = marked + 1
278 else
279 call doftable%set(arr(j), k)
280 dofs(k) = arr(j)
281 end if
282 else
283 dofs(k) = arr(j)
284 end if
285 end do
286 end select
287 end do
288
289 call device_memcpy(dofs, this%dof_d, total, host_to_device, sync = .true.)
290
291 deallocate(dofs)
292 call doftable%free()
293
294 end subroutine gs_device_shmem_buf_init
295
297 class(gs_device_shmem_buf_t), intent(inout) :: this
298
299
300 if (allocated(this%ndofs)) deallocate(this%ndofs)
301 if (allocated(this%offset)) deallocate(this%offset)
302 if (allocated(this%remote_offset)) deallocate(this%remote_offset)
303
304#ifdef HAVE_NVSHMEM
305 if (c_associated(this%buf_d)) call cudafree_nvshmem(this%buf_d)
306 if (c_associated(this%buf_v_d)) call cudafree_nvshmem(this%buf_v_d)
307#endif
308 if (c_associated(this%dof_d)) call device_free(this%dof_d)
309
310 end subroutine gs_device_shmem_buf_free
311
313 subroutine gs_device_shmem_init(this, send_pe, recv_pe)
314 class(gs_device_shmem_t), intent(inout) :: this
315 type(stack_i4_t), intent(inout) :: send_pe
316 type(stack_i4_t), intent(inout) :: recv_pe
317 integer :: i, nstrm, ierr
318 integer, allocatable :: local_offsets(:), remote_offsets(:)
319 integer(c_size_t) :: sz
320 integer(c_int64_t) :: i64_dummy
321
322 call this%init_order(send_pe, recv_pe)
323
324 ! The gs schedule registers every sharing peer for both directions, so
325 ! the two lists are identical and index-aligned. Both the per-peer
326 ! stream reuse below (stream(i) serves send_pe(i) and recv_pe(i)) and
327 ! the ungated repack of the parity send slab in nbsend (see there)
328 ! depend on this; fail loudly if a future schedule breaks it.
329 if (size(this%send_pe) .ne. size(this%recv_pe)) then
330 call neko_error('gs_device_shmem requires symmetric peer lists')
331 end if
332 do i = 1, size(this%send_pe)
333 if (this%send_pe(i) .ne. this%recv_pe(i)) then
334 call neko_error('gs_device_shmem requires aligned peer lists')
335 end if
336 end do
337
338 ! Send buffers are parity double-buffered (nslabs = 2), recv buffers
339 ! single (nslabs = 1); see gs_device_shmem_buf_init.
340 call this%send_buf%init(this%send_pe, this%send_dof, .false., 2)
341 call this%recv_buf%init(this%recv_pe, this%recv_dof, .true., 1)
342
343 ! Exchange, for every send peer, the offset in the receiver's recv buffer
344 ! where our slab must land. A single Alltoall keeps the exchange
345 ! deadlock-free for arbitrary, non-uniform peer sets (same rationale as
346 ! the host OpenSHMEM backend); the lazy pairwise exchange this replaces
347 ! indexed send_pe/recv_pe with the same loop index and required uniform,
348 ! index-aligned peer lists on every rank.
349 allocate(local_offsets(0:pe_size - 1))
350 allocate(remote_offsets(0:pe_size - 1))
351 local_offsets = -1
352 do i = 1, size(this%recv_pe)
353 local_offsets(this%recv_pe(i)) = this%recv_buf%offset(i)
354 end do
355 call mpi_alltoall(local_offsets, 1, mpi_integer, &
356 remote_offsets, 1, mpi_integer, neko_comm, ierr)
357 do i = 1, size(this%send_pe)
358 this%send_buf%remote_offset(i) = remote_offsets(this%send_pe(i))
359 end do
360 deallocate(local_offsets)
361 deallocate(remote_offsets)
362
363#if defined(HAVE_HIP) || defined(HAVE_CUDA)
364 ! Create a set of non-blocking streams. The per-peer streams, events and
365 ! notify signals are indexed over both send_pe (pack-and-push) and
366 ! recv_pe (unpack, sync), so size them for the larger of the two peer
367 ! lists.
368 nstrm = max(size(this%send_pe), size(this%recv_pe))
369 allocate(this%stream(nstrm))
370 do i = 1, nstrm
371 call device_stream_create_with_priority(this%stream(i), 1, &
373 end do
374
375 allocate(this%event(nstrm))
376 do i = 1, nstrm
377 call device_event_create(this%event(i), 2)
378 end do
379
380 call device_event_create(this%pack_event, 2)
381
382#ifdef HAVE_NVSHMEM
383 ! Rank-indexed symmetric signal arrays; zero-initialised by
384 ! cudamalloc_nvshmem, so the first round's CMP_GE waits on iter-1 = 0
385 ! pass immediately.
386 sz = c_sizeof(i64_dummy) * pe_size
387 call cudamalloc_nvshmem(this%done_sig_d, sz)
388 call cudamalloc_nvshmem(this%ready_sig_d, sz)
389#endif
390#endif
391
392 this%iter = 0
393 this%vec_supported = .true.
394
395 end subroutine gs_device_shmem_init
396
398 subroutine gs_device_shmem_free(this)
399 class(gs_device_shmem_t), intent(inout) :: this
400 integer :: i
401
402 call this%send_buf%free()
403 call this%recv_buf%free()
404
405#ifdef HAVE_NVSHMEM
406 ! Collective frees; every rank allocated both arrays exactly once.
407 if (c_associated(this%done_sig_d)) then
408 call cudafree_nvshmem(this%done_sig_d)
409 this%done_sig_d = c_null_ptr
410 end if
411 if (c_associated(this%ready_sig_d)) then
412 call cudafree_nvshmem(this%ready_sig_d)
413 this%ready_sig_d = c_null_ptr
414 end if
415#endif
416
417 call this%free_order()
418 call this%free_dofs()
419
420#if defined(HAVE_HIP) || defined(HAVE_CUDA)
421 if (allocated(this%stream)) then
422 do i = 1, size(this%stream)
423 call device_stream_destroy(this%stream(i))
424 end do
425 deallocate(this%stream)
426 end if
427
428 if (allocated(this%event)) then
429 do i = 1, size(this%event)
430 call device_event_destroy(this%event(i))
431 end do
432 deallocate(this%event)
433 end if
434
435 if (c_associated(this%pack_event)) then
436 call device_event_destroy(this%pack_event)
437 this%pack_event = c_null_ptr
438 end if
439#endif
440
441 end subroutine gs_device_shmem_free
442
444 subroutine gs_device_shmem_nbsend(this, u, n, tag, deps, strm)
445 class(gs_device_shmem_t), intent(inout) :: this
446 integer, intent(in) :: n
447 real(kind=rp), dimension(n), intent(inout) :: u
448 integer, intent(in) :: tag
449 type(c_ptr), intent(inout) :: deps
450 type(c_ptr), intent(inout) :: strm
451 integer :: i, parity
452 type(c_ptr) :: u_d
453
454 u_d = device_get_ptr(u)
455
456#ifdef HAVE_NVSHMEM
457 ! One round counter per gs op; advanced here so nbsend and nbwait agree
458 ! on the round's parity slab. The rank-indexed signal slots make the
459 ! exchange independent of peer counts and peer-list ordering.
460 this%iter = this%iter + 1
461 parity = mod(this%iter, 2)
462
463 ! Bulk-pack every peer's slab on the main stream, ordered after the
464 ! gather (deps), BEFORE any per-peer work. This orders all reads of the
465 ! shared buffer u before any unpack write to u. Packing inside the push
466 ! kernel, gated on the peer's ready signal, let an unpack from a fast
467 ! peer modify u before the pack for a slow peer had read it, so a dof
468 ! shared with both peers reached the slow peer already partially
469 ! reduced (divergence at large rank counts, where multi-peer dofs and
470 ! round-level skew are common).
471 !
472 ! The pack needs no remote gate: the parity slab was last put in round
473 ! iter-2, and this pack is stream-ordered (via strm's event joins at
474 ! the end of the previous nbwait) after the unpack of every peer's
475 ! round iter-1 slab. Peer i putting its round iter-1 slab here implies,
476 ! by stream order on peer i -- its push to us and its unpack of our
477 ! data run on one stream, since peer lists are index-aligned (checked
478 ! in init) -- that peer i consumed our round iter-2 slab, which in turn
479 ! implies that put has drained the parity slab.
480 call device_stream_wait_event(strm, deps, 0)
481 call cuda_gs_nvshmem_pack(u_d, this%send_buf%buf_d, &
482 this%send_buf%dof_d, parity * this%send_buf%slab_stride, &
483 this%send_buf%total, strm)
484 call device_event_record(this%pack_event, strm)
485
486 ! Order each per-peer stream after the pack; the push kernels in nbwait
487 ! read the packed parity slab.
488 do i = 1, size(this%send_pe)
489 call device_stream_wait_event(this%stream(i), this%pack_event, 0)
490 end do
491#endif
492
493 ! We do the rest in the "wait" routine below
494
495 end subroutine gs_device_shmem_nbsend
496
498 subroutine gs_device_shmem_nbrecv(this, tag)
499 class(gs_device_shmem_t), intent(inout) :: this
500 integer, intent(in) :: tag
501 integer :: i
502
503 ! We do everything in the "wait" routine below
504
505 end subroutine gs_device_shmem_nbrecv
506
508 subroutine gs_device_shmem_nbwait(this, u, n, op, strm)
509 class(gs_device_shmem_t), intent(inout) :: this
510 integer, intent(in) :: n
511 real(kind=rp), dimension(n), intent(inout) :: u
512 type(c_ptr), intent(inout) :: strm
513 integer :: op, done_req, i, parity
514 type(c_ptr) :: u_d
515
516 u_d = device_get_ptr(u)
517#ifdef HAVE_NVSHMEM
518 parity = mod(this%iter, 2)
519
520 ! Push the packed parity slab to every send peer. The kernel waits for
521 ! the peer's ready signal (previous round consumed, so its recv slab is
522 ! free) before the put; the pack already happened in nbsend.
523 do i = 1, size(this%send_pe)
524 call cuda_gs_push(this%send_buf%buf_d, &
525 parity * this%send_buf%slab_stride + this%send_buf%offset(i), &
526 this%send_buf%ndofs(i), &
527 this%stream(i), &
528 this%send_pe(i), &
529 this%recv_buf%buf_d, &
530 this%send_buf%remote_offset(i), &
531 this%iter, this%done_sig_d, this%ready_sig_d, pe_rank)
532 end do
533
534 ! For every recv peer: wait until its slab has landed, reduce it into u,
535 ! then post our ready signal so the peer may start its next round.
536 do done_req = 1, size(this%recv_pe)
537 call cuda_gs_push_wait(this%stream(done_req), this%iter, &
538 this%done_sig_d, this%recv_pe(done_req))
539 call cuda_gs_unpack(u_d, op, &
540 this%recv_buf%buf_d, &
541 this%recv_buf%dof_d, &
542 this%recv_buf%offset(done_req), &
543 this%recv_buf%ndofs(done_req), &
544 this%stream(done_req))
545 call cuda_gs_post_ready(this%stream(done_req), this%iter, &
546 this%ready_sig_d, pe_rank, this%recv_pe(done_req))
547 call device_event_record(this%event(done_req), this%stream(done_req))
548 end do
549
550 ! Sync non-blocking streams
551 do done_req = 1, size(this%recv_pe)
552 call device_stream_wait_event(strm, &
553 this%event(done_req), 0)
554 end do
555#endif
556 end subroutine gs_device_shmem_nbwait
557
562 subroutine gs_device_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
563 class(gs_device_shmem_t), intent(inout) :: this
564 integer, intent(in) :: n, nc
565 real(kind=rp), dimension(nc*n), intent(inout) :: u
566 integer, intent(in) :: tag
567 type(c_ptr), intent(inout) :: deps
568 type(c_ptr), intent(inout) :: strm
569 integer :: i, parity
570 type(c_ptr) :: u_d
571
572 u_d = device_get_ptr(u)
573
574#ifdef HAVE_NVSHMEM
575 ! Shared round counter with the scalar path; all ranks execute the same
576 ! op sequence, so counters stay in lockstep.
577 this%iter = this%iter + 1
578 parity = mod(this%iter, 2)
579
580 call device_stream_wait_event(strm, deps, 0)
581 call cuda_gs_nvshmem_pack_vec(u_d, this%send_buf%buf_v_d, &
582 this%send_buf%dof_d, &
583 parity * gs_vec_nc * this%send_buf%slab_stride, &
584 this%send_buf%total, nc, n, strm)
585 call device_event_record(this%pack_event, strm)
586
587 ! Order each per-peer stream after the pack; the push kernels in
588 ! nbwait_vec read the packed parity slab.
589 do i = 1, size(this%send_pe)
590 call device_stream_wait_event(this%stream(i), this%pack_event, 0)
591 end do
592#endif
593
594 end subroutine gs_device_shmem_nbsend_vec
595
597 subroutine gs_device_shmem_nbrecv_vec(this, tag, nc)
598 class(gs_device_shmem_t), intent(inout) :: this
599 integer, intent(in) :: tag, nc
600 end subroutine gs_device_shmem_nbrecv_vec
601
603 subroutine gs_device_shmem_nbwait_vec(this, u, n, nc, op, strm)
604 class(gs_device_shmem_t), intent(inout) :: this
605 integer, intent(in) :: n, nc
606 real(kind=rp), dimension(nc*n), intent(inout) :: u
607 type(c_ptr), intent(inout) :: strm
608 integer :: op, done_req, i, parity
609 type(c_ptr) :: u_d
610
611 u_d = device_get_ptr(u)
612#ifdef HAVE_NVSHMEM
613 parity = mod(this%iter, 2)
614
615 ! Push the packed nc-component parity slab to every send peer. Offsets
616 ! and counts are in elements (nc values per packed position); the recv
617 ! buffer holds a single slab, so only the local parity offset applies.
618 do i = 1, size(this%send_pe)
619 call cuda_gs_push(this%send_buf%buf_v_d, &
620 parity * gs_vec_nc * this%send_buf%slab_stride &
621 + nc * this%send_buf%offset(i), &
622 nc * this%send_buf%ndofs(i), &
623 this%stream(i), &
624 this%send_pe(i), &
625 this%recv_buf%buf_v_d, &
626 nc * this%send_buf%remote_offset(i), &
627 this%iter, this%done_sig_d, this%ready_sig_d, pe_rank)
628 end do
629
630 ! For every recv peer: wait until its slab has landed, reduce it into u,
631 ! then post our ready signal so the peer may start its next round.
632 do done_req = 1, size(this%recv_pe)
633 call cuda_gs_push_wait(this%stream(done_req), this%iter, &
634 this%done_sig_d, this%recv_pe(done_req))
635 call cuda_gs_unpack_vec(u_d, op, &
636 this%recv_buf%buf_v_d, &
637 this%recv_buf%dof_d, &
638 this%recv_buf%offset(done_req), &
639 this%recv_buf%ndofs(done_req), &
640 nc, n, &
641 this%stream(done_req))
642 call cuda_gs_post_ready(this%stream(done_req), this%iter, &
643 this%ready_sig_d, pe_rank, this%recv_pe(done_req))
644 call device_event_record(this%event(done_req), this%stream(done_req))
645 end do
646
647 ! Sync non-blocking streams
648 do done_req = 1, size(this%recv_pe)
649 call device_stream_wait_event(strm, &
650 this%event(done_req), 0)
651 end do
652#endif
653 end subroutine gs_device_shmem_nbwait_vec
654
655end module gs_device_shmem
Return the device pointer for an associated Fortran array.
Definition device.F90:113
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:62
integer, public pe_rank
MPI rank.
Definition comm.F90:59
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
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
subroutine, public device_event_destroy(event)
Destroy a device event.
Definition device.F90:1623
subroutine, public device_alloc(x_d, s)
Allocate memory on the device.
Definition device.F90:212
subroutine, public device_stream_create_with_priority(stream, flags, prio)
Create a device stream/command queue with priority.
Definition device.F90:1500
subroutine, public device_stream_wait_event(stream, event, flags)
Synchronize a device stream with an event.
Definition device.F90:1544
subroutine, public device_event_create(event, flags)
Create a device event queue.
Definition device.F90:1589
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:1522
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:50
Defines GPU aware MPI gather-scatter communication.
subroutine gs_device_shmem_nbrecv_vec(this, tag, nc)
No-op: everything happens in nbwait_vec.
subroutine gs_device_shmem_buf_init(this, pe_order, dof_stack, mark_dupes, nslabs)
subroutine gs_device_shmem_nbwait(this, u, n, op, strm)
Wait for non-blocking operations.
subroutine gs_device_shmem_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_shmem_nbsend(this, u, n, tag, deps, strm)
Post non-blocking send operations.
subroutine gs_device_shmem_nbrecv(this, tag)
Post non-blocking receive operations.
subroutine gs_device_shmem_nbwait_vec(this, u, n, nc, op, strm)
Fused nc-component push + unpack (the pack happens in nbsend_vec).
subroutine gs_device_shmem_free(this)
Deallocate MPI based communication method.
subroutine gs_device_shmem_buf_free(this)
subroutine gs_device_shmem_init(this, send_pe, recv_pe)
Initialise MPI based communication method.
Implements a hash table ADT.
Definition htable.f90:52
integer, parameter, public c_rp
Definition num_types.f90:15
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:53
Buffers for non-blocking communication and packing/unpacking.
Gather-scatter communication using device SHMEM. The arrays are indexed per PE like send_pe and @ rec...
Integer based hash table.
Definition htable.f90:102
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40