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