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
71 real(kind=
rp),
pointer :: send_buf(:) => null()
75 real(kind=
rp),
pointer :: recv_buf(:) => null()
77 integer,
allocatable :: send_len(:), recv_len(:)
78 integer,
allocatable :: send_offset(:), recv_offset(:)
80 integer(c_int64_t),
allocatable :: send_off64(:), send_len64(:)
84 integer(c_int64_t),
allocatable :: rmt_vcq_id(:), rmt_stadd(:)
85 integer(c_int64_t),
allocatable :: dst_off0(:), dst_off1(:)
86 integer(c_int),
allocatable :: dst_tag(:)
89 integer(c_int),
allocatable :: recv_indices(:)
90 integer(c_int) :: ncompleted = 0
92 integer :: buf_size = 0
96 integer :: send_size = 0
98 integer :: elem_size = 0
100 integer :: parity = 0
102 type(c_ptr) :: ctx = c_null_ptr
110 real(kind=
rp),
pointer :: send_buf_v(:) => null()
111 real(kind=
rp),
pointer :: recv_buf_v(:) => null()
116 integer(c_int64_t),
allocatable :: rmt_vcq_id_v(:), rmt_stadd_v(:)
117 integer(c_int64_t),
allocatable :: rmt_off_v(:), rmt_bufsz_v(:)
121 integer(c_int64_t),
allocatable :: send_off64_v(:), send_len64_v(:)
122 integer(c_int64_t),
allocatable :: dst_off_v(:)
124 integer :: parity_v = 0
126 type(c_ptr) :: ctx_v = c_null_ptr
140 integer(c_int64_t) :: gs_utofu_edata_max = 255
143 integer(c_int) :: gs_utofu_nrvcq = 1
145 logical,
save :: gs_utofu_logged = .false.
148 subroutine gs_utofu_init_ep(ntni_req, nthreads, ntni_out, nvcq_out, &
149 nrvcq_out, edata_max, ierr) bind(c, name = 'gs_utofu_init')
150 import :: c_int, c_int64_t
151 integer(c_int),
value :: ntni_req, nthreads
152 integer(c_int) :: ntni_out, nvcq_out, nrvcq_out
153 integer(c_int64_t) :: edata_max
154 integer(c_int) :: ierr
155 end subroutine gs_utofu_init_ep
158 recv_bytes, ctx, nrvcq, recv_vcq_id, recv_stadd, ierr) &
159 bind(c, name =
'gs_utofu_ctx_create')
160 import :: c_ptr, c_size_t, c_int64_t, c_int
161 type(c_ptr),
value :: send_buf, recv_buf
162 integer(c_size_t),
value :: send_bytes, recv_bytes
164 integer(c_int) :: nrvcq
165 integer(c_int64_t) :: recv_vcq_id(*)
166 integer(c_int64_t) :: recv_stadd(*)
167 integer(c_int) :: ierr
171 import :: c_ptr, c_int
172 type(c_ptr),
value :: ctx
173 integer(c_int) :: ierr
177 send_len, rmt_vcq_id, rmt_stadd, dst_off0, dst_off1, dst_tag, &
178 parity, send_base, elem_size, ierr) &
179 bind(c, name =
'gs_utofu_post_puts')
180 import :: c_ptr, c_int, c_int64_t
181 type(c_ptr),
value :: ctx
182 integer(c_int),
value :: tid, nteam, nsend, parity, elem_size
183 integer(c_int64_t) :: send_off(*), send_len(*)
184 integer(c_int64_t) :: rmt_vcq_id(*), rmt_stadd(*)
185 integer(c_int64_t) :: dst_off0(*), dst_off1(*)
186 integer(c_int) :: dst_tag(*)
187 integer(c_int64_t),
value :: send_base
188 integer(c_int) :: ierr
192 ierr) bind(c, name = 'gs_utofu_poll_recv')
193 import :: c_ptr, c_int
194 type(c_ptr),
value :: ctx
195 integer(c_int),
value :: parity, cap
196 integer(c_int) :: ncompleted
197 integer(c_int) :: out_idx(*)
198 integer(c_int) :: ierr
202 bind(c, name =
'gs_utofu_drain_half')
203 import :: c_ptr, c_int
204 type(c_ptr),
value :: ctx
205 integer(c_int),
value :: half, tid, nteam
206 integer(c_int) :: ierr
219 integer :: i, nsend, nrecv, send_total, recv_total
220 integer :: ntni_req, env_len, max_tag, nthreads
221 integer(c_int) :: ntni, nvcq, nrv, ierr
222 integer(c_int64_t),
allocatable :: recv_vcq_id(:), recv_stadd(:)
223 integer(c_size_t) :: send_bytes, recv_bytes
224 character(len=32) :: env_val
225 character(len=LOG_SIZE) :: log_buf
226 integer(c_int64_t),
allocatable :: msg_out(:,:), msg_in(:,:)
227 type(mpi_request),
allocatable :: sreq(:), rreq(:)
229 call this%init_order(send_pe, recv_pe)
231 nsend =
size(this%send_pe)
232 nrecv =
size(this%recv_pe)
233 this%elem_size = int(storage_size(1.0_rp) / 8)
235 allocate(this%send_len(nsend), this%send_offset(nsend))
236 allocate(this%send_off64(nsend), this%send_len64(nsend))
237 allocate(this%rmt_vcq_id(nsend), this%rmt_stadd(nsend))
238 allocate(this%dst_off0(nsend), this%dst_off1(nsend), this%dst_tag(nsend))
239 allocate(this%recv_len(nrecv), this%recv_offset(nrecv))
240 allocate(this%recv_indices(nrecv))
245 this%recv_len(i) = this%recv_dof(this%recv_pe(i))%size()
246 this%recv_offset(i) = recv_total
247 recv_total = recv_total + this%recv_len(i)
249 this%buf_size = recv_total
254 this%send_len(i) = this%send_dof(this%send_pe(i))%size()
255 this%send_offset(i) = send_total
256 this%send_off64(i) = int(send_total, c_int64_t)
257 this%send_len64(i) = int(this%send_len(i), c_int64_t)
258 send_total = send_total + this%send_len(i)
260 this%send_size = send_total
262 allocate(this%send_buf(
max(1, 2 * send_total)))
263 allocate(this%recv_buf(
max(1, 2 * recv_total)))
267 call get_environment_variable(
"NEKO_GS_UTOFU_NTNI", env_val, env_len)
268 if (env_len .gt. 0)
read(env_val(1:env_len), *) ntni_req
274 call gs_utofu_init_ep(int(ntni_req, c_int), int(nthreads, c_int), &
275 ntni, nvcq, gs_utofu_nrvcq, gs_utofu_edata_max, ierr)
276 if (ierr .ne. 0)
call neko_error(
"gs_utofu: endpoint init failed")
278 send_bytes = int(
size(this%send_buf), c_size_t) * this%elem_size
279 recv_bytes = int(
size(this%recv_buf), c_size_t) * this%elem_size
284 allocate(recv_vcq_id(gs_utofu_nrvcq), recv_stadd(gs_utofu_nrvcq))
285 recv_vcq_id = 0_c_int64_t
286 recv_stadd = 0_c_int64_t
289 c_loc(this%recv_buf), recv_bytes, this%ctx, nrv, recv_vcq_id, &
291 if (ierr .eq. 1)
then
292 call neko_error(
"gs_utofu: out of VCQs for the receive queues " // &
293 "(lower OMP_NUM_THREADS or NEKO_GS_UTOFU_NRVCQ, or set " // &
294 "NEKO_GS_UTOFU_NVCQ)")
295 else if (ierr .ne. 0)
then
296 call neko_error(
"gs_utofu: buffer registration failed")
302 if (.not. gs_utofu_logged .and.
pe_rank .eq. 0)
then
303 write(log_buf,
'(A,I0,A,I0,A,I0,A,I0,A)')
'uTofu inj. : ', &
304 int(nvcq),
' VCQs (', nthreads,
' threads) over ', int(ntni), &
305 ' TNIs (', ntni_req,
' requested)'
307 write(log_buf,
'(A,I0,A,I0,A)')
'uTofu recv : ', int(nrv), &
308 ' of ', int(gs_utofu_nrvcq),
' VCQs (first instance)'
310 gs_utofu_logged = .true.
316 allocate(msg_out(5,
max(1, nrecv)), msg_in(5,
max(1, nsend)))
317 allocate(sreq(
max(1, nrecv)), rreq(
max(1, nsend)))
322 msg_out(1, i) = recv_stadd(mod(i - 1, int(nrv)) + 1)
323 msg_out(2, i) = recv_vcq_id(mod(i - 1, int(nrv)) + 1)
324 msg_out(3, i) = int(this%recv_offset(i), c_int64_t)
325 msg_out(4, i) = int(this%buf_size + this%recv_offset(i), c_int64_t)
326 msg_out(5, i) = int(i - 1, c_int64_t)
330 call mpi_irecv(msg_in(:, i), 5, mpi_integer8, this%send_pe(i), &
334 call mpi_isend(msg_out(:, i), 5, mpi_integer8, this%recv_pe(i), &
337 if (nsend .gt. 0)
call mpi_waitall(nsend, rreq, mpi_statuses_ignore)
338 if (nrecv .gt. 0)
call mpi_waitall(nrecv, sreq, mpi_statuses_ignore)
342 this%rmt_stadd(i) = msg_in(1, i)
343 this%rmt_vcq_id(i) = msg_in(2, i)
344 this%dst_off0(i) = msg_in(3, i)
345 this%dst_off1(i) = msg_in(4, i)
346 this%dst_tag(i) = int(msg_in(5, i), c_int)
347 max_tag =
max(max_tag, this%dst_tag(i))
350 deallocate(msg_out, msg_in, sreq, rreq)
355 if (int(2 * max_tag + 1,
i8) .gt. int(gs_utofu_edata_max,
i8)) &
356 call neko_error(
"gs_utofu: neighbour count exceeds edata capacity")
360 allocate(this%send_buf_v(
max(1, 2 *
gs_vec_nc * send_total)))
361 allocate(this%recv_buf_v(
max(1, 2 *
gs_vec_nc * recv_total)))
362 allocate(this%rmt_vcq_id_v(nsend), this%rmt_stadd_v(nsend))
363 allocate(this%rmt_off_v(nsend), this%rmt_bufsz_v(nsend))
364 allocate(this%send_off64_v(nsend), this%send_len64_v(nsend))
365 allocate(this%dst_off_v(nsend))
367 send_bytes = int(
size(this%send_buf_v), c_size_t) * this%elem_size
368 recv_bytes = int(
size(this%recv_buf_v), c_size_t) * this%elem_size
369 recv_vcq_id = 0_c_int64_t
370 recv_stadd = 0_c_int64_t
373 c_loc(this%recv_buf_v), recv_bytes, this%ctx_v, nrv, recv_vcq_id, &
375 if (ierr .eq. 1)
then
376 call neko_error(
"gs_utofu: out of VCQs for the vector receive " // &
377 "queues (lower OMP_NUM_THREADS or NEKO_GS_UTOFU_NRVCQ, or " // &
378 "set NEKO_GS_UTOFU_NVCQ)")
379 else if (ierr .ne. 0)
then
380 call neko_error(
"gs_utofu: vector buffer registration failed")
388 allocate(msg_out(4,
max(1, nrecv)), msg_in(4,
max(1, nsend)))
389 allocate(sreq(
max(1, nrecv)), rreq(
max(1, nsend)))
393 msg_out(1, i) = recv_stadd(mod(i - 1, int(nrv)) + 1)
394 msg_out(2, i) = recv_vcq_id(mod(i - 1, int(nrv)) + 1)
395 msg_out(3, i) = int(this%recv_offset(i), c_int64_t)
396 msg_out(4, i) = int(this%buf_size, c_int64_t)
400 call mpi_irecv(msg_in(:, i), 4, mpi_integer8, this%send_pe(i), &
404 call mpi_isend(msg_out(:, i), 4, mpi_integer8, this%recv_pe(i), &
407 if (nsend .gt. 0)
call mpi_waitall(nsend, rreq, mpi_statuses_ignore)
408 if (nrecv .gt. 0)
call mpi_waitall(nrecv, sreq, mpi_statuses_ignore)
411 this%rmt_stadd_v(i) = msg_in(1, i)
412 this%rmt_vcq_id_v(i) = msg_in(2, i)
413 this%rmt_off_v(i) = msg_in(3, i)
414 this%rmt_bufsz_v(i) = msg_in(4, i)
417 deallocate(msg_out, msg_in, sreq, rreq)
422 call get_environment_variable(
"NEKO_GS_UTOFU_VEC", env_val, env_len)
423 this%vec_supported = .not. (env_len .gt. 0 .and. env_val(1:1) .eq.
'0')
425 call neko_error(
"uTofu support not built; reconfigure with --with-utofu")
433 integer(c_int) :: ierr
435 if (c_associated(this%ctx))
then
437 this%ctx = c_null_ptr
440 if (c_associated(this%ctx_v))
then
442 this%ctx_v = c_null_ptr
445 if (
associated(this%send_buf))
then
446 deallocate(this%send_buf)
449 if (
associated(this%recv_buf))
then
450 deallocate(this%recv_buf)
453 if (
associated(this%send_buf_v))
then
454 deallocate(this%send_buf_v)
457 if (
associated(this%recv_buf_v))
then
458 deallocate(this%recv_buf_v)
461 if (
allocated(this%send_len))
then
462 deallocate(this%send_len)
465 if (
allocated(this%recv_len))
then
466 deallocate(this%recv_len)
469 if (
allocated(this%send_offset))
then
470 deallocate(this%send_offset)
473 if (
allocated(this%recv_offset))
then
474 deallocate(this%recv_offset)
477 if (
allocated(this%send_off64))
then
478 deallocate(this%send_off64)
481 if (
allocated(this%send_len64))
then
482 deallocate(this%send_len64)
485 if (
allocated(this%rmt_vcq_id))
then
486 deallocate(this%rmt_vcq_id)
489 if (
allocated(this%rmt_stadd))
then
490 deallocate(this%rmt_stadd)
493 if (
allocated(this%dst_off0))
then
494 deallocate(this%dst_off0)
497 if (
allocated(this%dst_off1))
then
498 deallocate(this%dst_off1)
501 if (
allocated(this%dst_tag))
then
502 deallocate(this%dst_tag)
505 if (
allocated(this%recv_indices))
then
506 deallocate(this%recv_indices)
509 if (
allocated(this%rmt_vcq_id_v))
then
510 deallocate(this%rmt_vcq_id_v)
513 if (
allocated(this%rmt_stadd_v))
then
514 deallocate(this%rmt_stadd_v)
517 if (
allocated(this%rmt_off_v))
then
518 deallocate(this%rmt_off_v)
521 if (
allocated(this%rmt_bufsz_v))
then
522 deallocate(this%rmt_bufsz_v)
525 if (
allocated(this%send_off64_v))
then
526 deallocate(this%send_off64_v)
529 if (
allocated(this%send_len64_v))
then
530 deallocate(this%send_len64_v)
533 if (
allocated(this%dst_off_v))
then
534 deallocate(this%dst_off_v)
537 call this%free_order()
538 call this%free_dofs()
545 integer,
intent(in) :: n
546 real(kind=
rp),
dimension(n),
intent(inout) :: u
547 integer,
intent(in) :: tag
548 type(c_ptr),
intent(inout) :: deps
549 type(c_ptr),
intent(inout) :: strm
551 integer :: i, j, dst, off, ndst, send_base, tid, nteam
552 integer(c_int) :: ierr
553 integer,
pointer :: sp(:)
572 int(tid, c_int), int(nteam, c_int), ierr)
573 if (ierr .ne. 0)
call neko_error(
"gs_utofu: send drain failed")
581 send_base = this%parity * this%send_size
583 do i = 1,
size(this%send_pe)
584 dst = this%send_pe(i)
585 off = this%send_offset(i)
586 ndst = this%send_len(i)
587 sp => this%send_dof(dst)%array()
594 this%send_buf(send_base + off + j) = u(sp(j))
604 int(
size(this%send_pe), c_int), &
605 this%send_off64, this%send_len64, this%rmt_vcq_id, this%rmt_stadd, &
606 this%dst_off0, this%dst_off1, this%dst_tag, &
607 int(this%parity, c_int), int(send_base, c_int64_t), &
608 int(this%elem_size, c_int), ierr)
609 if (ierr .ne. 0)
call neko_error(
"gs_utofu: put failed")
620 integer,
intent(in) :: tag
627 integer,
intent(in) :: n
628 real(kind=
rp),
dimension(n),
intent(inout) :: u
629 type(c_ptr),
intent(inout) :: strm
632 integer :: i, j, k, src, off, nsrc, nreqs, base
633 integer(c_int) :: ierr
634 integer,
pointer ::
sp(:)
637 real(kind=
rp),
pointer :: rbuf(:)
639 base = this%parity * this%buf_size
640 nreqs =
size(this%recv_pe)
641 rbuf => this%recv_buf
643 do while (nreqs .gt. 0)
650 do while (this%ncompleted .eq. 0)
652 int(
size(this%recv_pe), c_int), this%ncompleted, &
653 this%recv_indices, ierr)
654 if (ierr .ne. 0)
call neko_error(
"gs_utofu: mrq poll failed")
659 do k = 1, this%ncompleted
660 i = this%recv_indices(k)
661 src = this%recv_pe(i)
662 off = this%recv_offset(i)
663 nsrc = this%recv_len(i)
664 sp => this%recv_dof(src)%array()
673 u(
sp(j)) = u(
sp(j)) + rbuf(base + off + j)
683 u(
sp(j)) = u(
sp(j)) * rbuf(base + off + j)
693 u(
sp(j)) = min(u(
sp(j)), rbuf(base + off + j))
703 u(
sp(j)) =
max(u(
sp(j)), rbuf(base + off + j))
707 call neko_error(
"Unknown operation in gs_nbwait_utofu")
711 nreqs = nreqs - this%ncompleted
721 this%parity = 1 - this%parity
736 integer,
intent(in) :: n, nc
737 real(kind=
rp),
dimension(nc*n),
intent(inout) :: u
738 integer,
intent(in) :: tag
739 type(c_ptr),
intent(inout) :: deps
740 type(c_ptr),
intent(inout) :: strm
742 integer :: i, j, c, dst, off, ndst, send_base, tid, nteam
743 integer(c_int) :: ierr
744 integer,
pointer :: sp(:)
754 int(tid, c_int), int(nteam, c_int), ierr)
755 if (ierr .ne. 0)
call neko_error(
"gs_utofu: vector send drain failed")
762 send_base = this%parity_v * (
gs_vec_nc * this%send_size)
764 do i = 1,
size(this%send_pe)
765 this%send_off64_v(i) = int(nc, c_int64_t) * this%send_off64(i)
766 this%send_len64_v(i) = int(nc, c_int64_t) * this%send_len64(i)
767 this%dst_off_v(i) = int(this%parity_v, c_int64_t) &
769 + int(nc, c_int64_t) * this%rmt_off_v(i)
775 do i = 1,
size(this%send_pe)
776 dst = this%send_pe(i)
777 off = this%send_offset(i)
778 ndst = this%send_len(i)
779 sp => this%send_dof(dst)%array()
787 this%send_buf_v(send_base + nc*off + (c-1)*ndst + j) = &
798 int(nteam, c_int), int(
size(this%send_pe), c_int), &
799 this%send_off64_v, this%send_len64_v, this%rmt_vcq_id_v, &
800 this%rmt_stadd_v, this%dst_off_v, this%dst_off_v, this%dst_tag, &
801 int(this%parity_v, c_int), int(send_base, c_int64_t), &
802 int(this%elem_size, c_int), ierr)
803 if (ierr .ne. 0)
call neko_error(
"gs_utofu: vector put failed")
814 integer,
intent(in) :: tag, nc
822 integer,
intent(in) :: n, nc
823 real(kind=
rp),
dimension(nc*n),
intent(inout) :: u
824 type(c_ptr),
intent(inout) :: strm
827 integer :: i, j, c, k, src, off, nsrc, nreqs, base
828 integer(c_int) :: ierr
829 integer,
pointer ::
sp(:)
830 real(kind=
rp),
pointer :: rbuf(:)
832 base = this%parity_v * (
gs_vec_nc * this%buf_size)
833 nreqs =
size(this%recv_pe)
834 rbuf => this%recv_buf_v
836 do while (nreqs .gt. 0)
840 do while (this%ncompleted .eq. 0)
842 int(
size(this%recv_pe), c_int), this%ncompleted, &
843 this%recv_indices, ierr)
845 call neko_error(
"gs_utofu: vector mrq poll failed")
850 do k = 1, this%ncompleted
851 i = this%recv_indices(k)
852 src = this%recv_pe(i)
853 off = this%recv_offset(i)
854 nsrc = this%recv_len(i)
855 sp => this%recv_dof(src)%array()
861 u((c-1)*n +
sp(j)) = u((c-1)*n +
sp(j)) + &
862 rbuf(base + nc*off + (c-1)*nsrc + j)
870 u((c-1)*n +
sp(j)) = u((c-1)*n +
sp(j)) * &
871 rbuf(base + nc*off + (c-1)*nsrc + j)
879 u((c-1)*n +
sp(j)) = min(u((c-1)*n +
sp(j)), &
880 rbuf(base + nc*off + (c-1)*nsrc + j))
888 u((c-1)*n +
sp(j)) =
max(u((c-1)*n +
sp(j)), &
889 rbuf(base + nc*off + (c-1)*nsrc + j))
894 call neko_error(
"Unknown operation in gs_nbwait_vec_utofu")
898 nreqs = nreqs - this%ncompleted
907 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...
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.