Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_shmem.F90
Go to the documentation of this file.
1! Copyright (c) 2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
35 use num_types, only : rp, c_rp, i8
36 use gs_comm, only : gs_comm_t, gs_vec_nc
38 use stack, only : stack_i4_t
39 use comm, only : neko_comm, pe_rank, pe_size
40 use mpi_f08, only : mpi_allreduce, mpi_alltoall, mpi_integer, mpi_max
41 use utils, only : neko_error
42#ifdef HAVE_OPENSHMEM
47#endif
48 use, intrinsic :: iso_c_binding, only : c_ptr, c_null_ptr, c_loc, &
49 c_f_pointer, c_associated, c_sizeof, c_size_t, c_int64_t
50 implicit none
51 private
52
56 integer, allocatable :: ndofs(:)
58 integer, allocatable :: offset(:)
61 integer, allocatable :: remote_offset(:)
63 integer :: total = 0
65 integer :: max_total = 0
67 type(c_ptr) :: buf_ptr = c_null_ptr
68 contains
69 procedure, pass(this) :: init => gs_shmem_buf_init
70 procedure, pass(this) :: free => gs_shmem_buf_free
71 end type gs_shmem_buf_t
72
91 type, public, extends(gs_comm_t) :: gs_shmem_t
92 type(gs_shmem_buf_t) :: send_buf
93 type(gs_shmem_buf_t) :: recv_buf
94 ! Symmetric data-arrival signals; data_signals[r] is set to `iter`
95 ! by PE r via shmem_putmem_signal_nbi when it has put data into
96 ! our recv buffer. Sized to pe_size on every PE.
97 type(c_ptr) :: data_signals_ptr = c_null_ptr
98 ! Symmetric ack signals; ack_signals[r] is set to `iter` by PE r
99 ! via shmem_uint64_atomic_set after it has consumed our most
100 ! recent put. Sized to pe_size on every PE.
101 type(c_ptr) :: ack_signals_ptr = c_null_ptr
102 ! Monotonically increasing iteration counter; sender writes this
103 ! value into the receiver's data signal slot via SHMEM_SIGNAL_SET,
104 ! and the receiver writes the same value into the sender's ack
105 ! slot after consumption.
106 integer(kind=i8) :: iter = 0
107 contains
108 procedure, pass(this) :: init => gs_shmem_init
109 procedure, pass(this) :: free => gs_shmem_free
110 procedure, pass(this) :: nbsend => gs_shmem_nbsend
111 procedure, pass(this) :: nbrecv => gs_shmem_nbrecv
112 procedure, pass(this) :: nbwait => gs_shmem_nbwait
113 procedure, pass(this) :: nbsend_vec => gs_shmem_nbsend_vec
114 procedure, pass(this) :: nbrecv_vec => gs_shmem_nbrecv_vec
115 procedure, pass(this) :: nbwait_vec => gs_shmem_nbwait_vec
116 end type gs_shmem_t
117
118contains
119
124 subroutine gs_shmem_buf_init(this, pe_order, dof_stack)
125 class(gs_shmem_buf_t), intent(inout) :: this
126 integer, intent(in) :: pe_order(:)
127 type(stack_i4_t), intent(inout) :: dof_stack(0:)
128 integer :: i, ierr, n
129 integer(c_size_t) :: sz
130 real(c_rp) :: rp_dummy
131#ifndef HAVE_OPENSHMEM
132 call neko_error('Neko was not built with OpenSHMEM support')
133#else
134
135 n = size(pe_order)
136
137 allocate(this%ndofs(n))
138 allocate(this%offset(n))
139 allocate(this%remote_offset(n))
140
141 do i = 1, n
142 this%remote_offset(i) = -1
143 end do
144
145 this%total = 0
146 do i = 1, n
147 this%ndofs(i) = dof_stack(pe_order(i))%size()
148 this%offset(i) = this%total
149 this%total = this%total + this%ndofs(i)
150 end do
151
152 ! OpenSHMEM symmetric memory must be allocated with the same size on
153 ! every PE.
154 call mpi_allreduce(this%total, this%max_total, 1, mpi_integer, mpi_max, &
155 neko_comm, ierr)
156
157 ! Sized for up to GS_VEC_NC components so the fused vector path can
158 ! reuse the same symmetric buffer; the scalar path uses the first
159 ! max_total elements.
160 sz = c_sizeof(rp_dummy) * int(max(gs_vec_nc*this%max_total, 1), c_size_t)
161 this%buf_ptr = shmem_malloc(sz)
162 if (.not. c_associated(this%buf_ptr)) then
163 call neko_error('shmem_malloc failed for gs_shmem buffer')
164 end if
165#endif
166 end subroutine gs_shmem_buf_init
167
169 subroutine gs_shmem_buf_free(this)
170 class(gs_shmem_buf_t), intent(inout) :: this
171
172 if (allocated(this%ndofs)) deallocate(this%ndofs)
173 if (allocated(this%offset)) deallocate(this%offset)
174 if (allocated(this%remote_offset)) deallocate(this%remote_offset)
175
176#ifdef HAVE_OPENSHMEM
177 if (c_associated(this%buf_ptr)) then
178 ! shmem_free is collective; gs_shmem_free issues a barrier before
179 ! tearing down buffers.
180 call shmem_free(this%buf_ptr)
181 end if
182#endif
183 this%buf_ptr = c_null_ptr
184 this%total = 0
185 this%max_total = 0
186
187 end subroutine gs_shmem_buf_free
188
190 subroutine gs_shmem_init(this, send_pe, recv_pe)
191 class(gs_shmem_t), intent(inout) :: this
192 type(stack_i4_t), intent(inout) :: send_pe
193 type(stack_i4_t), intent(inout) :: recv_pe
194 integer :: i, ierr
195 integer(c_size_t) :: i64_size
196 integer(c_int64_t) :: i64_dummy
197 integer, allocatable :: local_offsets(:), remote_offsets(:)
198#ifndef HAVE_OPENSHMEM
199 call neko_error('Neko was not built with OpenSHMEM support')
200#else
201
202 call this%free()
203 call this%init_order(send_pe, recv_pe)
204
205 call this%send_buf%init(this%send_pe, this%send_dof)
206 call this%recv_buf%init(this%recv_pe, this%recv_dof)
207
208 ! Allocate the per-rank symmetric signal arrays. Size pe_size on
209 ! every PE -> no Allreduce needed and no slot handshake required;
210 ! every PE writes/reads at offset = remote PE's rank.
211 i64_size = c_sizeof(i64_dummy)
212 this%data_signals_ptr = shmem_calloc(int(pe_size, c_size_t), i64_size)
213 if (.not. c_associated(this%data_signals_ptr)) then
214 call neko_error('shmem_calloc failed for gs_shmem data signals')
215 end if
216 this%ack_signals_ptr = shmem_calloc(int(pe_size, c_size_t), i64_size)
217 if (.not. c_associated(this%ack_signals_ptr)) then
218 call neko_error('shmem_calloc failed for gs_shmem ack signals')
219 end if
220
221 ! Offset exchange via Alltoall. send_pe and recv_pe are built in
222 ! gs_schedule with a shifted-modulo neighbor pattern; pairwise
223 ! Isend/Irecv keyed by neighbor rank turned out to deadlock at
224 ! certain PE counts. Alltoall is a single collective call that
225 ! cannot mismatch, and the payload is just pe_size ints per PE.
226 allocate(local_offsets(0:pe_size - 1))
227 allocate(remote_offsets(0:pe_size - 1))
228 local_offsets = -1
229 do i = 1, size(this%recv_pe)
230 local_offsets(this%recv_pe(i)) = this%recv_buf%offset(i)
231 end do
232 call mpi_alltoall(local_offsets, 1, mpi_integer, &
233 remote_offsets, 1, mpi_integer, neko_comm, ierr)
234 do i = 1, size(this%send_pe)
235 this%send_buf%remote_offset(i) = remote_offsets(this%send_pe(i))
236 end do
237 deallocate(local_offsets)
238 deallocate(remote_offsets)
239
240 this%iter = 0
241 this%vec_supported = .true.
242
243 ! Ensure all PEs have completed symmetric allocation before any
244 ! one-sided communication is issued.
245 call shmem_barrier_all()
246#endif
247 end subroutine gs_shmem_init
248
250 subroutine gs_shmem_free(this)
251 class(gs_shmem_t), intent(inout) :: this
252
253#ifdef HAVE_OPENSHMEM
254 ! shmem_free is collective; synchronize first to ensure no in-flight
255 ! puts target the buffers we are about to release.
256 call shmem_barrier_all()
257
258 if (c_associated(this%data_signals_ptr)) then
259 call shmem_free(this%data_signals_ptr)
260 end if
261 if (c_associated(this%ack_signals_ptr)) then
262 call shmem_free(this%ack_signals_ptr)
263 end if
264 this%data_signals_ptr = c_null_ptr
265 this%ack_signals_ptr = c_null_ptr
266 this%iter = 0
267#endif
268
269 call this%send_buf%free()
270 call this%recv_buf%free()
271
272 call this%free_order()
273 call this%free_dofs()
274
275 end subroutine gs_shmem_free
276
282 subroutine gs_shmem_nbsend(this, u, n, tag, deps, strm)
283 class(gs_shmem_t), intent(inout) :: this
284 integer, intent(in) :: n
285 real(kind=rp), dimension(n), intent(inout) :: u
286 integer, intent(in) :: tag
287 type(c_ptr), intent(inout) :: deps
288 type(c_ptr), intent(inout) :: strm
289 integer :: i, j, dst, base
290 integer(c_size_t) :: nbytes
291 integer , pointer :: sp(:)
292 real(kind=rp), pointer :: send_data(:), recv_data(:)
293 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
294 real(c_rp) :: rp_dummy
295#ifdef HAVE_OPENSHMEM
296
297 ! Each gs op gets a fresh signal value so receivers can distinguish
298 ! puts from one call vs. the next.
299 this%iter = this%iter + 1
300
301 call c_f_pointer(this%send_buf%buf_ptr, send_data, &
302 [max(this%send_buf%max_total, 1)])
303 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
304 [max(this%recv_buf%max_total, 1)])
305 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
306 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
307
308 do i = 1, size(this%send_pe)
309 dst = this%send_pe(i)
310
311 ! Wait for dst to have consumed our previous round's data before
312 ! we overwrite their recv buffer. ack_signals[dst] is calloc'd
313 ! to 0 and dst writes the iter value (via atomic_set) after
314 ! consumption, so for iter == 1 the wait succeeds immediately.
315 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
316 shmem_cmp_ge, this%iter - 1_8)
317
318 sp => this%send_dof(dst)%array()
319 base = this%send_buf%offset(i)
320 do concurrent(j = 1:this%send_dof(dst)%size())
321 send_data(base + j) = u(sp(j))
322 end do
323
324 nbytes = int(this%send_buf%ndofs(i), c_size_t) * c_sizeof(rp_dummy)
325 ! Put data + signal dst's data_signals[my_rank].
327 c_loc(recv_data(this%send_buf%remote_offset(i) + 1)), &
328 c_loc(send_data(base + 1)), &
329 nbytes, &
330 c_loc(data_signals(pe_rank + 1)), &
331 this%iter, shmem_signal_set, dst)
332 end do
333#endif
334 end subroutine gs_shmem_nbsend
335
337 subroutine gs_shmem_nbrecv(this, tag)
338 class(gs_shmem_t), intent(inout) :: this
339 integer, intent(in) :: tag
340
341 end subroutine gs_shmem_nbrecv
342
346 subroutine gs_shmem_nbwait(this, u, n, op, strm)
347 class(gs_shmem_t), intent(inout) :: this
348 integer, intent(in) :: n
349 real(kind=rp), dimension(n), intent(inout) :: u
350 type(c_ptr), intent(inout) :: strm
351 integer :: op
352 integer :: i, j, src, base
353 integer(c_int64_t) :: dummy
354 integer , pointer :: sp(:)
355 real(kind=rp), pointer :: recv_data(:)
356 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
357#ifdef HAVE_OPENSHMEM
358
359 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
360 [max(this%recv_buf%max_total, 1)])
361 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
362 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
363
364 do i = 1, size(this%recv_pe)
365 src = this%recv_pe(i)
366
367 ! Wait for data from src; data_signals[src] is set by src's put.
368 dummy = shmem_signal_wait_until(c_loc(data_signals(src + 1)), &
369 shmem_cmp_ge, this%iter)
370
371 sp => this%recv_dof(src)%array()
372 base = this%recv_buf%offset(i)
373 select case (op)
374 case (gs_op_add)
375 !NEC$ IVDEP
376 do concurrent(j = 1:this%recv_dof(src)%size())
377 u(sp(j)) = u(sp(j)) + recv_data(base + j)
378 end do
379 case (gs_op_mul)
380 !NEC$ IVDEP
381 do concurrent(j = 1:this%recv_dof(src)%size())
382 u(sp(j)) = u(sp(j)) * recv_data(base + j)
383 end do
384 case (gs_op_min)
385 !NEC$ IVDEP
386 do concurrent(j = 1:this%recv_dof(src)%size())
387 u(sp(j)) = min(u(sp(j)), recv_data(base + j))
388 end do
389 case (gs_op_max)
390 !NEC$ IVDEP
391 do concurrent(j = 1:this%recv_dof(src)%size())
392 u(sp(j)) = max(u(sp(j)), recv_data(base + j))
393 end do
394 case default
395 call neko_error("Unknown operation in gs_nbwait_shmem")
396 end select
397
398 ! Tell src we are done with this round's buffer so they may
399 ! overwrite it on the next round. We set our own rank's slot
400 ! in src's ack_signals; src waits there on the next nbsend.
402 c_loc(ack_signals(pe_rank + 1)), &
403 this%iter, src)
404 end do
405#endif
406 end subroutine gs_shmem_nbwait
407
412 subroutine gs_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
413 class(gs_shmem_t), intent(inout) :: this
414 integer, intent(in) :: n, nc
415 real(kind=rp), dimension(nc*n), intent(inout) :: u
416 integer, intent(in) :: tag
417 type(c_ptr), intent(inout) :: deps
418 type(c_ptr), intent(inout) :: strm
419 integer :: i, j, c, dst, base, ndst
420 integer(c_size_t) :: nbytes
421 integer, pointer :: sp(:)
422 real(kind=rp), pointer :: send_data(:), recv_data(:)
423 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
424 real(c_rp) :: rp_dummy
425#ifdef HAVE_OPENSHMEM
426
427 this%iter = this%iter + 1
428
429 call c_f_pointer(this%send_buf%buf_ptr, send_data, &
430 [max(nc*this%send_buf%max_total, 1)])
431 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
432 [max(nc*this%recv_buf%max_total, 1)])
433 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
434 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
435
436 do i = 1, size(this%send_pe)
437 dst = this%send_pe(i)
438
439 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
440 shmem_cmp_ge, this%iter - 1_8)
441
442 sp => this%send_dof(dst)%array()
443 base = this%send_buf%offset(i)
444 ndst = this%send_buf%ndofs(i)
445 do c = 1, nc
446 do concurrent(j = 1:ndst)
447 send_data(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
448 end do
449 end do
450
451 nbytes = int(nc*ndst, c_size_t) * c_sizeof(rp_dummy)
453 c_loc(recv_data(nc*this%send_buf%remote_offset(i) + 1)), &
454 c_loc(send_data(nc*base + 1)), &
455 nbytes, &
456 c_loc(data_signals(pe_rank + 1)), &
457 this%iter, shmem_signal_set, dst)
458 end do
459#endif
460 end subroutine gs_shmem_nbsend_vec
461
463 subroutine gs_shmem_nbrecv_vec(this, tag, nc)
464 class(gs_shmem_t), intent(inout) :: this
465 integer, intent(in) :: tag, nc
466 end subroutine gs_shmem_nbrecv_vec
467
470 subroutine gs_shmem_nbwait_vec(this, u, n, nc, op, strm)
471 class(gs_shmem_t), intent(inout) :: this
472 integer, intent(in) :: n, nc
473 real(kind=rp), dimension(nc*n), intent(inout) :: u
474 type(c_ptr), intent(inout) :: strm
475 integer :: op
476 integer :: i, j, c, src, base, nsrc
477 integer(c_int64_t) :: dummy
478 integer, pointer :: sp(:)
479 real(kind=rp), pointer :: recv_data(:)
480 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
481#ifdef HAVE_OPENSHMEM
482
483 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
484 [max(nc*this%recv_buf%max_total, 1)])
485 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
486 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
487
488 do i = 1, size(this%recv_pe)
489 src = this%recv_pe(i)
490
491 dummy = shmem_signal_wait_until(c_loc(data_signals(src + 1)), &
492 shmem_cmp_ge, this%iter)
493
494 sp => this%recv_dof(src)%array()
495 base = this%recv_buf%offset(i)
496 nsrc = this%recv_buf%ndofs(i)
497 select case (op)
498 case (gs_op_add)
499 do c = 1, nc
500 !NEC$ IVDEP
501 do concurrent(j = 1:nsrc)
502 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
503 recv_data(nc*base + (c-1)*nsrc + j)
504 end do
505 end do
506 case (gs_op_mul)
507 do c = 1, nc
508 !NEC$ IVDEP
509 do concurrent(j = 1:nsrc)
510 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
511 recv_data(nc*base + (c-1)*nsrc + j)
512 end do
513 end do
514 case (gs_op_min)
515 do c = 1, nc
516 !NEC$ IVDEP
517 do concurrent(j = 1:nsrc)
518 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
519 recv_data(nc*base + (c-1)*nsrc + j))
520 end do
521 end do
522 case (gs_op_max)
523 do c = 1, nc
524 !NEC$ IVDEP
525 do concurrent(j = 1:nsrc)
526 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
527 recv_data(nc*base + (c-1)*nsrc + j))
528 end do
529 end do
530 case default
531 call neko_error("Unknown operation in gs_shmem_nbwait_vec")
532 end select
533
535 c_loc(ack_signals(pe_rank + 1)), &
536 this%iter, src)
537 end do
538#endif
539 end subroutine gs_shmem_nbwait_vec
540
541end module gs_shmem
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
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 Gather-scatter operations.
Definition gs_ops.f90:34
integer, parameter, public gs_op_add
Definition gs_ops.f90:36
integer, parameter, public gs_op_max
Definition gs_ops.f90:36
integer, parameter, public gs_op_min
Definition gs_ops.f90:36
integer, parameter, public gs_op_mul
Definition gs_ops.f90:36
Defines OpenSHMEM gather-scatter communication.
Definition gs_shmem.F90:34
subroutine gs_shmem_buf_init(this, pe_order, dof_stack)
Allocate symmetric memory and per-neighbor bookkeeping for one direction of communication.
Definition gs_shmem.F90:125
subroutine gs_shmem_nbrecv_vec(this, tag, nc)
No-op: receives are completed via remote put-with-signal.
Definition gs_shmem.F90:464
subroutine gs_shmem_init(this, send_pe, recv_pe)
Initialise OpenSHMEM based communication method.
Definition gs_shmem.F90:191
subroutine gs_shmem_nbwait_vec(this, u, n, nc, op, strm)
Fused nc-component wait/reduce: per peer, wait on the data signal and reduce nc component blocks into...
Definition gs_shmem.F90:471
subroutine gs_shmem_nbsend(this, u, n, tag, deps, strm)
Pack the gathered shared dofs into the symmetric send buffer and issue non-blocking puts with signali...
Definition gs_shmem.F90:283
subroutine gs_shmem_buf_free(this)
Release symmetric memory and bookkeeping.
Definition gs_shmem.F90:170
subroutine gs_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
Fused nc-component send: pack nc contiguous component blocks per peer slab and put nc*ndofs reals wit...
Definition gs_shmem.F90:413
subroutine gs_shmem_free(this)
Deallocate OpenSHMEM based communication method.
Definition gs_shmem.F90:251
subroutine gs_shmem_nbrecv(this, tag)
No-op: receives are completed via remote put-with-signal.
Definition gs_shmem.F90:338
subroutine gs_shmem_nbwait(this, u, n, op, strm)
Wait per-neighbor for the signal indicating that data has landed, apply the gather-scatter operation ...
Definition gs_shmem.F90:347
integer, parameter, public i8
Definition num_types.f90:7
integer, parameter, public sp
Definition num_types.f90:8
integer, parameter, public c_rp
Definition num_types.f90:13
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Fortran bindings to SHMEM's C API.
Definition shmem.F90:34
@ shmem_signal_set
Definition shmem.F90:79
@ shmem_cmp_ge
Definition shmem.F90:50
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:52
Symmetric buffer for one direction of OpenSHMEM communication.
Definition gs_shmem.F90:54
Gather-scatter communication using OpenSHMEM one-sided puts with per-rank signaling for completion (O...
Definition gs_shmem.F90:91
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40