51 use mpi_f08,
only : mpi_request, mpi_isend, mpi_irecv, mpi_waitall, &
52 mpi_integer8, mpi_statuses_ignore
57 use,
intrinsic :: iso_c_binding
80 real(kind=
rp),
pointer :: send_buf(:) => null()
84 real(kind=
rp),
pointer :: recv_buf(:) => null()
86 integer,
allocatable :: send_len(:), recv_len(:)
87 integer,
allocatable :: send_offset(:), recv_offset(:)
89 integer(c_int64_t),
allocatable :: send_off64(:), send_len64(:)
93 integer(c_int64_t),
allocatable :: rmt_vcq_id(:), rmt_stadd(:)
94 integer(c_int64_t),
allocatable :: dst_off0(:), dst_off1(:)
95 integer(c_int),
allocatable :: dst_tag(:)
98 integer(c_int),
allocatable :: recv_indices(:)
99 integer(c_int) :: ncompleted = 0
101 integer :: buf_size = 0
105 integer :: send_size = 0
107 integer :: elem_size = 0
109 integer :: parity = 0
111 type(c_ptr) :: ctx = c_null_ptr
119 real(kind=
rp),
pointer :: send_buf_v(:) => null()
120 real(kind=
rp),
pointer :: recv_buf_v(:) => null()
125 integer(c_int64_t),
allocatable :: rmt_vcq_id_v(:), rmt_stadd_v(:)
126 integer(c_int64_t),
allocatable :: rmt_off_v(:), rmt_bufsz_v(:)
130 integer(c_int64_t),
allocatable :: send_off64_v(:), send_len64_v(:)
131 integer(c_int64_t),
allocatable :: dst_off_v(:)
133 integer :: parity_v = 0
135 type(c_ptr) :: ctx_v = c_null_ptr
149 integer(c_int64_t) :: gs_utofu_edata_max = 255
152 integer(c_int) :: gs_utofu_nrvcq = 1
154 logical,
save :: gs_utofu_logged = .false.
157 subroutine gs_utofu_init_ep(ntni_req, nthreads, ntni_out, nvcq_out, &
158 nrvcq_out, edata_max, ierr) bind(c, name = 'gs_utofu_init')
159 import :: c_int, c_int64_t
160 integer(c_int),
value :: ntni_req, nthreads
161 integer(c_int) :: ntni_out, nvcq_out, nrvcq_out
162 integer(c_int64_t) :: edata_max
163 integer(c_int) :: ierr
164 end subroutine gs_utofu_init_ep
167 recv_bytes, ctx, nrvcq, recv_vcq_id, recv_stadd, ierr) &
168 bind(c, name =
'gs_utofu_ctx_create')
169 import :: c_ptr, c_size_t, c_int64_t, c_int
170 type(c_ptr),
value :: send_buf, recv_buf
171 integer(c_size_t),
value :: send_bytes, recv_bytes
173 integer(c_int) :: nrvcq
174 integer(c_int64_t) :: recv_vcq_id(*)
175 integer(c_int64_t) :: recv_stadd(*)
176 integer(c_int) :: ierr
180 import :: c_ptr, c_int
181 type(c_ptr),
value :: ctx
182 integer(c_int) :: ierr
186 send_len, rmt_vcq_id, rmt_stadd, dst_off0, dst_off1, dst_tag, &
187 parity, send_base, elem_size, ierr) &
188 bind(c, name =
'gs_utofu_post_puts')
189 import :: c_ptr, c_int, c_int64_t
190 type(c_ptr),
value :: ctx
191 integer(c_int),
value :: tid, nteam, nsend, parity, elem_size
192 integer(c_int64_t) :: send_off(*), send_len(*)
193 integer(c_int64_t) :: rmt_vcq_id(*), rmt_stadd(*)
194 integer(c_int64_t) :: dst_off0(*), dst_off1(*)
195 integer(c_int) :: dst_tag(*)
196 integer(c_int64_t),
value :: send_base
197 integer(c_int) :: ierr
201 ierr) bind(c, name = 'gs_utofu_poll_recv')
202 import :: c_ptr, c_int
203 type(c_ptr),
value :: ctx
204 integer(c_int),
value :: parity, cap
205 integer(c_int) :: ncompleted
206 integer(c_int) :: out_idx(*)
207 integer(c_int) :: ierr
211 bind(c, name =
'gs_utofu_drain_half')
212 import :: c_ptr, c_int
213 type(c_ptr),
value :: ctx
214 integer(c_int),
value :: half, tid, nteam
215 integer(c_int) :: ierr
228 integer :: i, nsend, nrecv, send_total, recv_total
229 integer :: ntni_req, env_len, max_tag, nthreads
230 integer(c_int) :: ntni, nvcq, nrv, ierr
231 integer(c_int64_t),
allocatable :: recv_vcq_id(:), recv_stadd(:)
232 integer(c_size_t) :: send_bytes, recv_bytes
233 character(len=32) :: env_val
234 character(len=LOG_SIZE) :: log_buf
235 integer(c_int64_t),
allocatable :: msg_out(:,:), msg_in(:,:)
236 type(mpi_request),
allocatable :: sreq(:), rreq(:)
238 call this%init_order(send_pe, recv_pe)
240 nsend =
size(this%send_pe)
241 nrecv =
size(this%recv_pe)
242 this%elem_size = int(storage_size(1.0_rp) / 8)
244 allocate(this%send_len(nsend), this%send_offset(nsend))
245 allocate(this%send_off64(nsend), this%send_len64(nsend))
246 allocate(this%rmt_vcq_id(nsend), this%rmt_stadd(nsend))
247 allocate(this%dst_off0(nsend), this%dst_off1(nsend), this%dst_tag(nsend))
248 allocate(this%recv_len(nrecv), this%recv_offset(nrecv))
249 allocate(this%recv_indices(nrecv))
254 this%recv_len(i) = this%recv_dof(this%recv_pe(i))%size()
255 this%recv_offset(i) = recv_total
256 recv_total = recv_total + this%recv_len(i)
258 this%buf_size = recv_total
263 this%send_len(i) = this%send_dof(this%send_pe(i))%size()
264 this%send_offset(i) = send_total
265 this%send_off64(i) = int(send_total, c_int64_t)
266 this%send_len64(i) = int(this%send_len(i), c_int64_t)
267 send_total = send_total + this%send_len(i)
269 this%send_size = send_total
271 allocate(this%send_buf(
max(1, 2 * send_total)))
272 allocate(this%recv_buf(
max(1, 2 * recv_total)))
276 call get_environment_variable(
"NEKO_GS_UTOFU_NTNI", env_val, env_len)
277 if (env_len .gt. 0)
read(env_val(1:env_len), *) ntni_req
283 call gs_utofu_init_ep(int(ntni_req, c_int), int(nthreads, c_int), &
284 ntni, nvcq, gs_utofu_nrvcq, gs_utofu_edata_max, ierr)
285 if (ierr .ne. 0)
call neko_error(
"gs_utofu: endpoint init failed")
287 send_bytes = int(
size(this%send_buf), c_size_t) * this%elem_size
288 recv_bytes = int(
size(this%recv_buf), c_size_t) * this%elem_size
293 allocate(recv_vcq_id(gs_utofu_nrvcq), recv_stadd(gs_utofu_nrvcq))
294 recv_vcq_id = 0_c_int64_t
295 recv_stadd = 0_c_int64_t
298 c_loc(this%recv_buf), recv_bytes, this%ctx, nrv, recv_vcq_id, &
300 if (ierr .eq. 1)
then
301 call neko_error(
"gs_utofu: out of VCQs for the receive queues " // &
302 "(lower OMP_NUM_THREADS or NEKO_GS_UTOFU_NRVCQ, or set " // &
303 "NEKO_GS_UTOFU_NVCQ)")
304 else if (ierr .ne. 0)
then
305 call neko_error(
"gs_utofu: buffer registration failed")
311 if (.not. gs_utofu_logged .and.
pe_rank .eq. 0)
then
312 write(log_buf,
'(A,I0,A,I0,A,I0,A,I0,A)')
'uTofu inj. : ', &
313 int(nvcq),
' VCQs (', nthreads,
' threads) over ', int(ntni), &
314 ' TNIs (', ntni_req,
' requested)'
316 write(log_buf,
'(A,I0,A,I0,A)')
'uTofu recv : ', int(nrv), &
317 ' of ', int(gs_utofu_nrvcq),
' VCQs (first instance)'
319 gs_utofu_logged = .true.
325 allocate(msg_out(5,
max(1, nrecv)), msg_in(5,
max(1, nsend)))
326 allocate(sreq(
max(1, nrecv)), rreq(
max(1, nsend)))
331 msg_out(1, i) = recv_stadd(mod(i - 1, int(nrv)) + 1)
332 msg_out(2, i) = recv_vcq_id(mod(i - 1, int(nrv)) + 1)
333 msg_out(3, i) = int(this%recv_offset(i), c_int64_t)
334 msg_out(4, i) = int(this%buf_size + this%recv_offset(i), c_int64_t)
335 msg_out(5, i) = int(i - 1, c_int64_t)
339 call mpi_irecv(msg_in(:, i), 5, mpi_integer8, this%send_pe(i), &
343 call mpi_isend(msg_out(:, i), 5, mpi_integer8, this%recv_pe(i), &
346 if (nsend .gt. 0)
call mpi_waitall(nsend, rreq, mpi_statuses_ignore)
347 if (nrecv .gt. 0)
call mpi_waitall(nrecv, sreq, mpi_statuses_ignore)
351 this%rmt_stadd(i) = msg_in(1, i)
352 this%rmt_vcq_id(i) = msg_in(2, i)
353 this%dst_off0(i) = msg_in(3, i)
354 this%dst_off1(i) = msg_in(4, i)
355 this%dst_tag(i) = int(msg_in(5, i), c_int)
356 max_tag =
max(max_tag, this%dst_tag(i))
359 deallocate(msg_out, msg_in, sreq, rreq)
364 if (int(2 * max_tag + 1,
i8) .gt. int(gs_utofu_edata_max,
i8)) &
365 call neko_error(
"gs_utofu: neighbour count exceeds edata capacity")
369 allocate(this%send_buf_v(
max(1, 2 *
gs_vec_nc * send_total)))
370 allocate(this%recv_buf_v(
max(1, 2 *
gs_vec_nc * recv_total)))
371 allocate(this%rmt_vcq_id_v(nsend), this%rmt_stadd_v(nsend))
372 allocate(this%rmt_off_v(nsend), this%rmt_bufsz_v(nsend))
373 allocate(this%send_off64_v(nsend), this%send_len64_v(nsend))
374 allocate(this%dst_off_v(nsend))
376 send_bytes = int(
size(this%send_buf_v), c_size_t) * this%elem_size
377 recv_bytes = int(
size(this%recv_buf_v), c_size_t) * this%elem_size
378 recv_vcq_id = 0_c_int64_t
379 recv_stadd = 0_c_int64_t
382 c_loc(this%recv_buf_v), recv_bytes, this%ctx_v, nrv, recv_vcq_id, &
384 if (ierr .eq. 1)
then
385 call neko_error(
"gs_utofu: out of VCQs for the vector receive " // &
386 "queues (lower OMP_NUM_THREADS or NEKO_GS_UTOFU_NRVCQ, or " // &
387 "set NEKO_GS_UTOFU_NVCQ)")
388 else if (ierr .ne. 0)
then
389 call neko_error(
"gs_utofu: vector buffer registration failed")
397 allocate(msg_out(4,
max(1, nrecv)), msg_in(4,
max(1, nsend)))
398 allocate(sreq(
max(1, nrecv)), rreq(
max(1, nsend)))
402 msg_out(1, i) = recv_stadd(mod(i - 1, int(nrv)) + 1)
403 msg_out(2, i) = recv_vcq_id(mod(i - 1, int(nrv)) + 1)
404 msg_out(3, i) = int(this%recv_offset(i), c_int64_t)
405 msg_out(4, i) = int(this%buf_size, c_int64_t)
409 call mpi_irecv(msg_in(:, i), 4, mpi_integer8, this%send_pe(i), &
413 call mpi_isend(msg_out(:, i), 4, mpi_integer8, this%recv_pe(i), &
416 if (nsend .gt. 0)
call mpi_waitall(nsend, rreq, mpi_statuses_ignore)
417 if (nrecv .gt. 0)
call mpi_waitall(nrecv, sreq, mpi_statuses_ignore)
420 this%rmt_stadd_v(i) = msg_in(1, i)
421 this%rmt_vcq_id_v(i) = msg_in(2, i)
422 this%rmt_off_v(i) = msg_in(3, i)
423 this%rmt_bufsz_v(i) = msg_in(4, i)
426 deallocate(msg_out, msg_in, sreq, rreq)
431 call get_environment_variable(
"NEKO_GS_UTOFU_VEC", env_val, env_len)
432 this%vec_supported = .not. (env_len .gt. 0 .and. env_val(1:1) .eq.
'0')
434 call neko_error(
"uTofu support not built; reconfigure with --with-utofu")
442 integer(c_int) :: ierr
444 if (c_associated(this%ctx))
then
446 this%ctx = c_null_ptr
449 if (c_associated(this%ctx_v))
then
451 this%ctx_v = c_null_ptr
454 if (
associated(this%send_buf))
then
455 deallocate(this%send_buf)
458 if (
associated(this%recv_buf))
then
459 deallocate(this%recv_buf)
462 if (
associated(this%send_buf_v))
then
463 deallocate(this%send_buf_v)
466 if (
associated(this%recv_buf_v))
then
467 deallocate(this%recv_buf_v)
470 if (
allocated(this%send_len))
then
471 deallocate(this%send_len)
474 if (
allocated(this%recv_len))
then
475 deallocate(this%recv_len)
478 if (
allocated(this%send_offset))
then
479 deallocate(this%send_offset)
482 if (
allocated(this%recv_offset))
then
483 deallocate(this%recv_offset)
486 if (
allocated(this%send_off64))
then
487 deallocate(this%send_off64)
490 if (
allocated(this%send_len64))
then
491 deallocate(this%send_len64)
494 if (
allocated(this%rmt_vcq_id))
then
495 deallocate(this%rmt_vcq_id)
498 if (
allocated(this%rmt_stadd))
then
499 deallocate(this%rmt_stadd)
502 if (
allocated(this%dst_off0))
then
503 deallocate(this%dst_off0)
506 if (
allocated(this%dst_off1))
then
507 deallocate(this%dst_off1)
510 if (
allocated(this%dst_tag))
then
511 deallocate(this%dst_tag)
514 if (
allocated(this%recv_indices))
then
515 deallocate(this%recv_indices)
518 if (
allocated(this%rmt_vcq_id_v))
then
519 deallocate(this%rmt_vcq_id_v)
522 if (
allocated(this%rmt_stadd_v))
then
523 deallocate(this%rmt_stadd_v)
526 if (
allocated(this%rmt_off_v))
then
527 deallocate(this%rmt_off_v)
530 if (
allocated(this%rmt_bufsz_v))
then
531 deallocate(this%rmt_bufsz_v)
534 if (
allocated(this%send_off64_v))
then
535 deallocate(this%send_off64_v)
538 if (
allocated(this%send_len64_v))
then
539 deallocate(this%send_len64_v)
542 if (
allocated(this%dst_off_v))
then
543 deallocate(this%dst_off_v)
546 call this%free_order()
547 call this%free_dofs()
554 integer,
intent(in) :: n
555 real(kind=
rp),
dimension(n),
intent(inout) :: u
556 integer,
intent(in) :: tag
557 type(c_ptr),
intent(inout) :: deps
558 type(c_ptr),
intent(inout) :: strm
560 integer :: i, j, dst, off, ndst, send_base, tid, nteam
561 integer(c_int) :: ierr
562 integer,
pointer :: sp(:)
581 int(tid, c_int), int(nteam, c_int), ierr)
582 if (ierr .ne. 0)
call neko_error(
"gs_utofu: send drain failed")
590 send_base = this%parity * this%send_size
592 do i = 1,
size(this%send_pe)
593 dst = this%send_pe(i)
594 off = this%send_offset(i)
595 ndst = this%send_len(i)
596 sp => this%send_dof(dst)%array()
604 this%send_buf(send_base + off + j) = u(sp(j))
614 int(
size(this%send_pe), c_int), &
615 this%send_off64, this%send_len64, this%rmt_vcq_id, this%rmt_stadd, &
616 this%dst_off0, this%dst_off1, this%dst_tag, &
617 int(this%parity, c_int), int(send_base, c_int64_t), &
618 int(this%elem_size, c_int), ierr)
619 if (ierr .ne. 0)
call neko_error(
"gs_utofu: put failed")
630 integer,
intent(in) :: tag
637 integer,
intent(in) :: n
638 real(kind=
rp),
dimension(n),
intent(inout) :: u
639 type(c_ptr),
intent(inout) :: strm
642 integer :: i, j, k, src, off, nsrc, nreqs, base
643 integer(c_int) :: ierr
644 integer,
pointer ::
sp(:)
647 real(kind=
rp),
pointer :: rbuf(:)
649 base = this%parity * this%buf_size
650 nreqs =
size(this%recv_pe)
651 rbuf => this%recv_buf
653 do while (nreqs .gt. 0)
660 do while (this%ncompleted .eq. 0)
662 int(
size(this%recv_pe), c_int), this%ncompleted, &
663 this%recv_indices, ierr)
664 if (ierr .ne. 0)
call neko_error(
"gs_utofu: mrq poll failed")
669 do k = 1, this%ncompleted
670 i = this%recv_indices(k)
671 src = this%recv_pe(i)
672 off = this%recv_offset(i)
673 nsrc = this%recv_len(i)
674 sp => this%recv_dof(src)%array()
684 u(
sp(j)) = u(
sp(j)) + rbuf(base + off + j)
695 u(
sp(j)) = u(
sp(j)) * rbuf(base + off + j)
706 u(
sp(j)) = min(u(
sp(j)), rbuf(base + off + j))
717 u(
sp(j)) =
max(u(
sp(j)), rbuf(base + off + j))
721 call neko_error(
"Unknown operation in gs_nbwait_utofu")
725 nreqs = nreqs - this%ncompleted
735 this%parity = 1 - this%parity
750 integer,
intent(in) :: n, nc
751 real(kind=
rp),
dimension(nc*n),
intent(inout) :: u
752 integer,
intent(in) :: tag
753 type(c_ptr),
intent(inout) :: deps
754 type(c_ptr),
intent(inout) :: strm
756 integer :: i, j, c, dst, off, ndst, send_base, tid, nteam
757 integer(c_int) :: ierr
758 integer,
pointer :: sp(:)
768 int(tid, c_int), int(nteam, c_int), ierr)
769 if (ierr .ne. 0)
call neko_error(
"gs_utofu: vector send drain failed")
776 send_base = this%parity_v * (
gs_vec_nc * this%send_size)
778 do i = 1,
size(this%send_pe)
779 this%send_off64_v(i) = int(nc, c_int64_t) * this%send_off64(i)
780 this%send_len64_v(i) = int(nc, c_int64_t) * this%send_len64(i)
781 this%dst_off_v(i) = int(this%parity_v, c_int64_t) &
783 + int(nc, c_int64_t) * this%rmt_off_v(i)
789 do i = 1,
size(this%send_pe)
790 dst = this%send_pe(i)
791 off = this%send_offset(i)
792 ndst = this%send_len(i)
793 sp => this%send_dof(dst)%array()
802 this%send_buf_v(send_base + nc*off + (c-1)*ndst + j) = &
813 int(nteam, c_int), int(
size(this%send_pe), c_int), &
814 this%send_off64_v, this%send_len64_v, this%rmt_vcq_id_v, &
815 this%rmt_stadd_v, this%dst_off_v, this%dst_off_v, this%dst_tag, &
816 int(this%parity_v, c_int), int(send_base, c_int64_t), &
817 int(this%elem_size, c_int), ierr)
818 if (ierr .ne. 0)
call neko_error(
"gs_utofu: vector put failed")
829 integer,
intent(in) :: tag, nc
837 integer,
intent(in) :: n, nc
838 real(kind=
rp),
dimension(nc*n),
intent(inout) :: u
839 type(c_ptr),
intent(inout) :: strm
842 integer :: i, j, c, k, src, off, nsrc, nreqs, base
843 integer(c_int) :: ierr
844 integer,
pointer ::
sp(:)
845 real(kind=
rp),
pointer :: rbuf(:)
847 base = this%parity_v * (
gs_vec_nc * this%buf_size)
848 nreqs =
size(this%recv_pe)
849 rbuf => this%recv_buf_v
851 do while (nreqs .gt. 0)
855 do while (this%ncompleted .eq. 0)
857 int(
size(this%recv_pe), c_int), this%ncompleted, &
858 this%recv_indices, ierr)
860 call neko_error(
"gs_utofu: vector mrq poll failed")
865 do k = 1, this%ncompleted
866 i = this%recv_indices(k)
867 src = this%recv_pe(i)
868 off = this%recv_offset(i)
869 nsrc = this%recv_len(i)
870 sp => this%recv_dof(src)%array()
876 u((c-1)*n +
sp(j)) = u((c-1)*n +
sp(j)) + &
877 rbuf(base + nc*off + (c-1)*nsrc + j)
885 u((c-1)*n +
sp(j)) = u((c-1)*n +
sp(j)) * &
886 rbuf(base + nc*off + (c-1)*nsrc + j)
894 u((c-1)*n +
sp(j)) = min(u((c-1)*n +
sp(j)), &
895 rbuf(base + nc*off + (c-1)*nsrc + j))
903 u((c-1)*n +
sp(j)) =
max(u((c-1)*n +
sp(j)), &
904 rbuf(base + nc*off + (c-1)*nsrc + j))
909 call neko_error(
"Unknown operation in gs_nbwait_vec_utofu")
913 nreqs = nreqs - this%ncompleted
922 this%parity_v = 1 - this%parity_v
void gs_utofu_post_puts(void *vctx, int tid, int nteam, int nsend, const int64_t *send_off, const int64_t *send_len, const uint64_t *rmt_vcq_id, const uint64_t *rmt_stadd, const int64_t *dst_off0, const int64_t *dst_off1, const int *dst_tag, int parity, int64_t send_base, int elem_size, int *ierr)
void gs_utofu_ctx_free(void *vctx, int *ierr)
void gs_utofu_poll_recv(void *vctx, int parity, int cap, int *ncompleted, int *out_idx, int *ierr)
void gs_utofu_ctx_create(void *send_buf, size_t send_bytes, void *recv_buf, size_t recv_bytes, void **ctx_out, int *nrvcq_out, uint64_t *recv_vcq_id, uint64_t *recv_stadd, int *ierr)
void gs_utofu_drain_half(void *vctx, int half, int tid, int nteam, int *ierr)
integer, public pe_rank
MPI rank.
type(mpi_comm), public neko_comm
MPI communicator.
Defines a gather-scatter communication method.
integer, parameter, public gs_vec_nc
Maximum number of components handled by the fused vector (multi-component) halo exchange used by gs_o...
integer, parameter, public gs_comm_utofu
Defines Gather-scatter operations.
integer, parameter, public gs_op_add
integer, parameter, public gs_op_max
integer, parameter, public gs_op_min
integer, parameter, public gs_op_mul
Defines a gather-scatter backend using the native Tofu interconnect (uTofu). Each rank registers its ...
integer, parameter gs_utofu_xchg_tag
MPI tag used for the one-off neighbour metadata exchange at init.
subroutine gs_utofu_init(this, send_pe, recv_pe)
Initialise the uTofu communication method. See gs_comm.f90 for details.
subroutine gs_nbsend_vec_utofu(this, u, n, nc, tag, deps, strm)
Pack the fused nc-component send slabs and fire the puts (non-blocking).
integer, parameter gs_utofu_xchg_tag_v
Ditto for the fused vector path's metadata exchange.
subroutine gs_nbwait_vec_utofu(this, u, n, nc, op, strm)
Poll for incoming vector puts, reducing each nc-component slab into u as it lands,...
subroutine gs_nbwait_utofu(this, u, n, op, strm)
Poll for incoming puts, reducing each slab into u as it lands, then wait for our own sends to drain a...
logical, parameter, public gs_utofu_avail
Whether uTofu support was built into this Neko (–with-utofu). Lets callers (e.g. the gs comm....
subroutine gs_nbsend_utofu(this, u, n, tag, deps, strm)
Pack the send slabs and fire all one-sided puts (non-blocking).
subroutine gs_nbrecv_vec_utofu(this, tag, nc)
No-op: the vector receive buffer is registered once at init, so there is nothing to post per round.
subroutine gs_nbrecv_utofu(this, tag)
No-op: the one-sided receive buffer is registered once at init, so there is nothing to post per round...
subroutine gs_utofu_free(this)
Deallocate the uTofu communication method.
type(log_t), public neko_log
Global log stream.
integer, parameter, public log_size
integer, parameter, public i8
integer, parameter, public sp
integer, parameter, public rp
Global precision used in computations.
Implements a dynamic stack ADT.
Gather-scatter communication method.
Gather-scatter communication using one-sided uTofu puts.