Neko 1.99.5
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
39 use device
40 use comm, only : pe_size, pe_rank, neko_comm
41 use mpi_f08, only : mpi_allreduce, mpi_alltoall, mpi_integer, mpi_max
42 use utils, only : neko_error
43 use, intrinsic :: iso_c_binding, only : c_sizeof, c_int32_t, c_int64_t, &
44 c_ptr, c_null_ptr, c_size_t, c_associated
45 implicit none
46 private
47
49 type, private :: gs_device_shmem_buf_t
50 integer, allocatable :: ndofs(:)
51 integer, allocatable :: offset(:)
52 integer, allocatable :: remote_offset(:)
53 integer :: total
54 type(c_ptr) :: buf_d = c_null_ptr
55 type(c_ptr) :: buf_v_d = c_null_ptr
56 type(c_ptr) :: dof_d = c_null_ptr
57 contains
58 procedure, pass(this) :: init => gs_device_shmem_buf_init
59 procedure, pass(this) :: free => gs_device_shmem_buf_free
61
64 type, public, extends(gs_comm_t) :: gs_device_shmem_t
65 type(gs_device_shmem_buf_t) :: send_buf
66 type(gs_device_shmem_buf_t) :: recv_buf
67 type(c_ptr), allocatable :: stream(:)
68 type(c_ptr), allocatable :: event(:)
73 integer :: iter = 0
80 type(c_ptr) :: done_sig_d = c_null_ptr
81 type(c_ptr) :: ready_sig_d = c_null_ptr
82 contains
83 procedure, pass(this) :: init => gs_device_shmem_init
84 procedure, pass(this) :: free => gs_device_shmem_free
85 procedure, pass(this) :: nbsend => gs_device_shmem_nbsend
86 procedure, pass(this) :: nbrecv => gs_device_shmem_nbrecv
87 procedure, pass(this) :: nbwait => gs_device_shmem_nbwait
88 procedure, pass(this) :: nbsend_vec => gs_device_shmem_nbsend_vec
89 procedure, pass(this) :: nbrecv_vec => gs_device_shmem_nbrecv_vec
90 procedure, pass(this) :: nbwait_vec => gs_device_shmem_nbwait_vec
91 end type gs_device_shmem_t
92
93
94#if defined (HAVE_CUDA) && defined(HAVE_NVSHMEM)
95
96 interface
97 subroutine cudamalloc_nvshmem(ptr, size) &
98 bind(c, name = 'cudamalloc_nvshmem')
99 use, intrinsic :: iso_c_binding
100 implicit none
101 type(c_ptr) :: ptr
102 integer(c_size_t), value :: size
103 end subroutine cudamalloc_nvshmem
104 end interface
105
106 interface
107 subroutine cudafree_nvshmem(ptr) &
108 bind(c, name = 'cudafree_nvshmem')
109 use, intrinsic :: iso_c_binding
110 implicit none
111 type(c_ptr) :: ptr
112 end subroutine cudafree_nvshmem
113 end interface
114
115 interface
116 subroutine cuda_gs_pack_and_push(u_d, buf_d, dof_d, offset, n, stream, &
117 dest_rank, rbuf_d, roffset, iter, done_d, ready_d, mype) &
118 bind(c, name = 'cuda_gs_pack_and_push')
119 use, intrinsic :: iso_c_binding
120 implicit none
121 integer(c_int), value :: n, offset, dest_rank, roffset, iter, mype
122 type(c_ptr), value :: u_d, buf_d, dof_d, stream, rbuf_d, done_d, &
123 ready_d
124 end subroutine cuda_gs_pack_and_push
125 end interface
126
127 interface
128 subroutine cuda_gs_pack_and_push_wait(stream, iter, done_d, src_rank) &
129 bind(c, name = 'cuda_gs_pack_and_push_wait')
130 use, intrinsic :: iso_c_binding
131 implicit none
132 integer(c_int), value :: iter, src_rank
133 type(c_ptr), value :: stream, done_d
134 end subroutine cuda_gs_pack_and_push_wait
135 end interface
136
137 interface
138 subroutine cuda_gs_post_ready(stream, iter, ready_d, mype, src_rank) &
139 bind(c, name = 'cuda_gs_post_ready')
140 use, intrinsic :: iso_c_binding
141 implicit none
142 integer(c_int), value :: iter, mype, src_rank
143 type(c_ptr), value :: stream, ready_d
144 end subroutine cuda_gs_post_ready
145 end interface
146
147 interface
148 subroutine cuda_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
149 bind(c, name = 'cuda_gs_unpack')
150 use, intrinsic :: iso_c_binding
151 implicit none
152 integer(c_int), value :: op, offset, n
153 type(c_ptr), value :: u_d, buf_d, dof_d, stream
154 end subroutine cuda_gs_unpack
155 end interface
156
157 interface
158 subroutine cuda_gs_pack_and_push_vec(u_d, buf_d, dof_d, offset, n, nc, &
159 ns, stream, dest_rank, rbuf_d, roffset, iter, done_d, ready_d, &
160 mype) bind(c, name = 'cuda_gs_pack_and_push_vec')
161 use, intrinsic :: iso_c_binding
162 implicit none
163 integer(c_int), value :: n, nc, ns, offset, dest_rank, roffset, iter
164 integer(c_int), value :: mype
165 type(c_ptr), value :: u_d, buf_d, dof_d, stream, rbuf_d, done_d, &
166 ready_d
167 end subroutine cuda_gs_pack_and_push_vec
168 end interface
169
170 interface
171 subroutine cuda_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
172 stream) bind(c, name = 'cuda_gs_unpack_vec')
173 use, intrinsic :: iso_c_binding
174 implicit none
175 integer(c_int), value :: op, offset, n, nc, ns
176 type(c_ptr), value :: u_d, buf_d, dof_d, stream
177 end subroutine cuda_gs_unpack_vec
178 end interface
179#endif
180
181contains
182
183 subroutine gs_device_shmem_buf_init(this, pe_order, dof_stack, mark_dupes)
184 class(gs_device_shmem_buf_t), intent(inout) :: this
185 integer, allocatable, intent(inout) :: pe_order(:)
186 type(stack_i4_t), allocatable, intent(inout) :: dof_stack(:)
187 logical, intent(in) :: mark_dupes
188 integer, allocatable :: dofs(:)
189 integer :: i, j, total, max_total
190 integer(c_size_t) :: sz
191 type(htable_i4_t) :: doftable
192 integer :: dupe, marked, k
193 real(c_rp) :: rp_dummy
194 integer(c_int32_t) :: i4_dummy
195
196 allocate(this%ndofs(size(pe_order)))
197 allocate(this%offset(size(pe_order)))
198 allocate(this%remote_offset(size(pe_order)))
199
200 do i = 1, size(pe_order)
201 this%remote_offset(i) = -1
202 end do
203
204 total = 0
205 do i = 1, size(pe_order)
206 this%ndofs(i) = dof_stack(pe_order(i))%size()
207 this%offset(i) = total
208 total = total + this%ndofs(i)
209 end do
210
211 call mpi_allreduce(total, max_total, 1, mpi_integer, mpi_max, neko_comm)
212
213 this%total = total
214
215 sz = c_sizeof(rp_dummy) * max_total
216#ifdef HAVE_NVSHMEM
217 call cudamalloc_nvshmem(this%buf_d, sz)
218 ! Fused vector symmetric buffer, sized for up to GS_VEC_NC components.
219 sz = c_sizeof(rp_dummy) * gs_vec_nc * max_total
220 call cudamalloc_nvshmem(this%buf_v_d, sz)
221#endif
222
223 sz = c_sizeof(i4_dummy) * total
224 call device_alloc(this%dof_d, sz)
225
226 if (mark_dupes) call doftable%init(2*total)
227 allocate(dofs(total))
228
229 ! Copy from dof_stack into dofs, optionally marking duplicates with doftable
230 marked = 0
231 do i = 1, size(pe_order)
232 ! %array() breaks on cray
233 select type (arr => dof_stack(pe_order(i))%data)
234 type is (integer)
235 do j = 1, this%ndofs(i)
236 k = this%offset(i) + j
237 if (mark_dupes) then
238 if (doftable%get(arr(j), dupe) .eq. 0) then
239 if (dofs(dupe) .gt. 0) then
240 dofs(dupe) = -dofs(dupe)
241 marked = marked + 1
242 end if
243 dofs(k) = -arr(j)
244 marked = marked + 1
245 else
246 call doftable%set(arr(j), k)
247 dofs(k) = arr(j)
248 end if
249 else
250 dofs(k) = arr(j)
251 end if
252 end do
253 end select
254 end do
255
256 call device_memcpy(dofs, this%dof_d, total, host_to_device, sync = .true.)
257
258 deallocate(dofs)
259 call doftable%free()
260
261 end subroutine gs_device_shmem_buf_init
262
264 class(gs_device_shmem_buf_t), intent(inout) :: this
265
266
267 if (allocated(this%ndofs)) deallocate(this%ndofs)
268 if (allocated(this%offset)) deallocate(this%offset)
269
270#ifdef HAVE_NVSHMEM
271 if (c_associated(this%buf_d)) call cudafree_nvshmem(this%buf_d)
272 if (c_associated(this%buf_v_d)) call cudafree_nvshmem(this%buf_v_d)
273#endif
274 if (c_associated(this%dof_d)) call device_free(this%dof_d)
275
276 end subroutine gs_device_shmem_buf_free
277
279 subroutine gs_device_shmem_init(this, send_pe, recv_pe)
280 class(gs_device_shmem_t), intent(inout) :: this
281 type(stack_i4_t), intent(inout) :: send_pe
282 type(stack_i4_t), intent(inout) :: recv_pe
283 integer :: i, nstrm, ierr
284 integer, allocatable :: local_offsets(:), remote_offsets(:)
285 integer(c_size_t) :: sz
286 integer(c_int64_t) :: i64_dummy
287
288 call this%init_order(send_pe, recv_pe)
289
290 call this%send_buf%init(this%send_pe, this%send_dof, .false.)
291 call this%recv_buf%init(this%recv_pe, this%recv_dof, .true.)
292
293 ! Exchange, for every send peer, the offset in the receiver's recv buffer
294 ! where our slab must land. A single Alltoall keeps the exchange
295 ! deadlock-free for arbitrary, non-uniform peer sets (same rationale as
296 ! the host OpenSHMEM backend); the lazy pairwise exchange this replaces
297 ! indexed send_pe/recv_pe with the same loop index and required uniform,
298 ! index-aligned peer lists on every rank.
299 allocate(local_offsets(0:pe_size - 1))
300 allocate(remote_offsets(0:pe_size - 1))
301 local_offsets = -1
302 do i = 1, size(this%recv_pe)
303 local_offsets(this%recv_pe(i)) = this%recv_buf%offset(i)
304 end do
305 call mpi_alltoall(local_offsets, 1, mpi_integer, &
306 remote_offsets, 1, mpi_integer, neko_comm, ierr)
307 do i = 1, size(this%send_pe)
308 this%send_buf%remote_offset(i) = remote_offsets(this%send_pe(i))
309 end do
310 deallocate(local_offsets)
311 deallocate(remote_offsets)
312
313#if defined(HAVE_HIP) || defined(HAVE_CUDA)
314 ! Create a set of non-blocking streams. The per-peer streams, events and
315 ! notify signals are indexed over both send_pe (pack-and-push) and
316 ! recv_pe (unpack, sync), so size them for the larger of the two peer
317 ! lists.
318 nstrm = max(size(this%send_pe), size(this%recv_pe))
319 allocate(this%stream(nstrm))
320 do i = 1, nstrm
321 call device_stream_create_with_priority(this%stream(i), 1, &
323 end do
324
325 allocate(this%event(nstrm))
326 do i = 1, nstrm
327 call device_event_create(this%event(i), 2)
328 end do
329
330#ifdef HAVE_NVSHMEM
331 ! Rank-indexed symmetric signal arrays; zero-initialised by
332 ! cudamalloc_nvshmem, so the first round's CMP_GE waits on iter-1 = 0
333 ! pass immediately.
334 sz = c_sizeof(i64_dummy) * pe_size
335 call cudamalloc_nvshmem(this%done_sig_d, sz)
336 call cudamalloc_nvshmem(this%ready_sig_d, sz)
337#endif
338#endif
339
340 this%iter = 0
341 this%vec_supported = .true.
342
343 end subroutine gs_device_shmem_init
344
346 subroutine gs_device_shmem_free(this)
347 class(gs_device_shmem_t), intent(inout) :: this
348 integer :: i
349
350 call this%send_buf%free()
351 call this%recv_buf%free()
352
353#ifdef HAVE_NVSHMEM
354 ! Collective frees; every rank allocated both arrays exactly once.
355 if (c_associated(this%done_sig_d)) then
356 call cudafree_nvshmem(this%done_sig_d)
357 this%done_sig_d = c_null_ptr
358 end if
359 if (c_associated(this%ready_sig_d)) then
360 call cudafree_nvshmem(this%ready_sig_d)
361 this%ready_sig_d = c_null_ptr
362 end if
363#endif
364
365 call this%free_order()
366 call this%free_dofs()
367
368#if defined(HAVE_HIP) || defined(HAVE_CUDA)
369 if (allocated(this%stream)) then
370 do i = 1, size(this%stream)
371 call device_stream_destroy(this%stream(i))
372 end do
373 deallocate(this%stream)
374 end if
375#endif
376
377 end subroutine gs_device_shmem_free
378
380 subroutine gs_device_shmem_nbsend(this, u, n, tag, deps, strm)
381 class(gs_device_shmem_t), intent(inout) :: this
382 integer, intent(in) :: n
383 real(kind=rp), dimension(n), intent(inout) :: u
384 integer, intent(in) :: tag
385 type(c_ptr), intent(inout) :: deps
386 type(c_ptr), intent(inout) :: strm
387 integer :: i
388 type(c_ptr) :: u_d
389
390 u_d = device_get_ptr(u)
391
392 ! Order each per-peer stream after the gather (deps); the pack-and-push
393 ! kernels in nbwait are stream-ordered behind this wait. The historic
394 ! host-side device_sync here papered over a send-buffer reuse race: the
395 ! pack-and-push kernel used to overwrite the send buffer BEFORE its
396 ! ready handshake, while the previous round's non-blocking put could
397 ! still be draining it. The kernel now performs the handshake before
398 ! packing (see gs_nvshmem_kernels.h), which closes that race properly,
399 ! so no host synchronisation is needed.
400 do i = 1, size(this%send_pe)
401 call device_stream_wait_event(this%stream(i), deps, 0)
402 end do
403
404 ! We do the rest in the "wait" routine below
405
406 end subroutine gs_device_shmem_nbsend
407
409 subroutine gs_device_shmem_nbrecv(this, tag)
410 class(gs_device_shmem_t), intent(inout) :: this
411 integer, intent(in) :: tag
412 integer :: i
413
414 ! We do everything in the "wait" routine below
415
416 end subroutine gs_device_shmem_nbrecv
417
419 subroutine gs_device_shmem_nbwait(this, u, n, op, strm)
420 class(gs_device_shmem_t), intent(inout) :: this
421 integer, intent(in) :: n
422 real(kind=rp), dimension(n), intent(inout) :: u
423 type(c_ptr), intent(inout) :: strm
424 integer :: op, done_req, i
425 type(c_ptr) :: u_d
426
427 u_d = device_get_ptr(u)
428#ifdef HAVE_NVSHMEM
429 ! One round counter per gs op; the rank-indexed signal slots make the
430 ! exchange independent of peer counts and peer-list ordering.
431 this%iter = this%iter + 1
432
433 ! Push our slab to every send peer. The kernel waits for the peer's
434 ! ready signal (previous round consumed) before overwriting the send
435 ! slab, then puts with a done signal.
436 do i = 1, size(this%send_pe)
437 call cuda_gs_pack_and_push(u_d, &
438 this%send_buf%buf_d, &
439 this%send_buf%dof_d, &
440 this%send_buf%offset(i), &
441 this%send_buf%ndofs(i), &
442 this%stream(i), &
443 this%send_pe(i), &
444 this%recv_buf%buf_d, &
445 this%send_buf%remote_offset(i), &
446 this%iter, this%done_sig_d, this%ready_sig_d, pe_rank)
447 end do
448
449 ! For every recv peer: wait until its slab has landed, reduce it into u,
450 ! then post our ready signal so the peer may start its next round.
451 do done_req = 1, size(this%recv_pe)
452 call cuda_gs_pack_and_push_wait(this%stream(done_req), this%iter, &
453 this%done_sig_d, this%recv_pe(done_req))
454 call cuda_gs_unpack(u_d, op, &
455 this%recv_buf%buf_d, &
456 this%recv_buf%dof_d, &
457 this%recv_buf%offset(done_req), &
458 this%recv_buf%ndofs(done_req), &
459 this%stream(done_req))
460 call cuda_gs_post_ready(this%stream(done_req), this%iter, &
461 this%ready_sig_d, pe_rank, this%recv_pe(done_req))
462 call device_event_record(this%event(done_req), this%stream(done_req))
463 end do
464
465 ! Sync non-blocking streams
466 do done_req = 1, size(this%recv_pe)
467 call device_stream_wait_event(strm, &
468 this%event(done_req), 0)
469 end do
470#endif
471 end subroutine gs_device_shmem_nbwait
472
476 subroutine gs_device_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
477 class(gs_device_shmem_t), intent(inout) :: this
478 integer, intent(in) :: n, nc
479 real(kind=rp), dimension(nc*n), intent(inout) :: u
480 integer, intent(in) :: tag
481 type(c_ptr), intent(inout) :: deps
482 type(c_ptr), intent(inout) :: strm
483 integer :: i
484
485 ! Order each per-peer stream after the gather/copies (deps); no host
486 ! synchronisation needed, see gs_device_shmem_nbsend.
487 do i = 1, size(this%send_pe)
488 call device_stream_wait_event(this%stream(i), deps, 0)
489 end do
490
491 end subroutine gs_device_shmem_nbsend_vec
492
494 subroutine gs_device_shmem_nbrecv_vec(this, tag, nc)
495 class(gs_device_shmem_t), intent(inout) :: this
496 integer, intent(in) :: tag, nc
497 end subroutine gs_device_shmem_nbrecv_vec
498
500 subroutine gs_device_shmem_nbwait_vec(this, u, n, nc, op, strm)
501 class(gs_device_shmem_t), intent(inout) :: this
502 integer, intent(in) :: n, nc
503 real(kind=rp), dimension(nc*n), intent(inout) :: u
504 type(c_ptr), intent(inout) :: strm
505 integer :: op, done_req, i
506 type(c_ptr) :: u_d
507
508 u_d = device_get_ptr(u)
509#ifdef HAVE_NVSHMEM
510 ! One round counter per gs op (shared with the scalar path; all ranks
511 ! execute the same op sequence, so counters stay in lockstep).
512 this%iter = this%iter + 1
513
514 ! Push our nc-component slab to every send peer.
515 do i = 1, size(this%send_pe)
516 call cuda_gs_pack_and_push_vec(u_d, &
517 this%send_buf%buf_v_d, &
518 this%send_buf%dof_d, &
519 this%send_buf%offset(i), &
520 this%send_buf%ndofs(i), &
521 nc, n, &
522 this%stream(i), &
523 this%send_pe(i), &
524 this%recv_buf%buf_v_d, &
525 this%send_buf%remote_offset(i), &
526 this%iter, this%done_sig_d, this%ready_sig_d, pe_rank)
527 end do
528
529 ! For every recv peer: wait until its slab has landed, reduce it into u,
530 ! then post our ready signal so the peer may start its next round.
531 do done_req = 1, size(this%recv_pe)
532 call cuda_gs_pack_and_push_wait(this%stream(done_req), this%iter, &
533 this%done_sig_d, this%recv_pe(done_req))
534 call cuda_gs_unpack_vec(u_d, op, &
535 this%recv_buf%buf_v_d, &
536 this%recv_buf%dof_d, &
537 this%recv_buf%offset(done_req), &
538 this%recv_buf%ndofs(done_req), &
539 nc, n, &
540 this%stream(done_req))
541 call cuda_gs_post_ready(this%stream(done_req), this%iter, &
542 this%ready_sig_d, pe_rank, this%recv_pe(done_req))
543 call device_event_record(this%event(done_req), this%stream(done_req))
544 end do
545
546 ! Sync non-blocking streams
547 do done_req = 1, size(this%recv_pe)
548 call device_stream_wait_event(strm, &
549 this%event(done_req), 0)
550 end do
551#endif
552 end subroutine gs_device_shmem_nbwait_vec
553
554end module gs_device_shmem
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
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:45
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 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_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 pack-and-push + unpack.
subroutine gs_device_shmem_buf_init(this, pe_order, dof_stack, mark_dupes)
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