51 integer,
parameter :: GS_TUNE_BCKND(11) = [gs_comm_mpi, gs_comm_neighbour, &
52 gs_comm_mpirma, gs_comm_openshmem, gs_comm_caf, gs_comm_utofu, &
53 gs_comm_crystal, gs_comm_mpigpu, gs_comm_nccl, gs_comm_crystalgpu, &
60 logical,
parameter :: GS_TUNE_DEFAULT(11) = [.true., .true., .true., &
61 .true., .false., .true., .true., .true., .true., .true., .false.]
68 logical,
parameter :: GS_TUNE_DEV_AVAIL = (
neko_bcknd_hip .eq. 1) .or. &
76 logical,
save :: caf_signal_tuned = .false.
89 function gs_comm_cand()
result(cand)
90 integer,
allocatable :: cand(:)
91 character(len=LOG_SIZE) :: log_buf
92 character(len=13) :: label
93 logical :: sel(size(GS_TUNE_BCKND)), named(size(GS_TUNE_BCKND))
94 integer :: c(size(GS_TUNE_BCKND)), i, n
96 call gs_tune_select(sel, named)
99 do i = 1,
size(gs_tune_bcknd)
100 if (.not. sel(i)) cycle
101 if (gs_comm_tunable(gs_tune_bcknd(i)))
then
103 c(n) = gs_tune_bcknd(i)
104 else if (named(i))
then
108 label = adjustl(gs_comm_name(gs_tune_bcknd(i)))
109 write(log_buf,
'(A,A,A12)') label,
': ',
'unavailable'
110 call neko_log%message(log_buf)
117 end function gs_comm_cand
128 function gs_comm_tunable(comm_bcknd)
result(tunable)
129 integer,
intent(in) :: comm_bcknd
132 select case (comm_bcknd)
133 case (gs_comm_mpi, gs_comm_neighbour)
135 case (gs_comm_crystal)
142 case (gs_comm_mpirma)
153 case (gs_comm_openshmem)
164 (pe_size .eq. global_pe_size)
167 case (gs_comm_mpigpu, gs_comm_crystalgpu)
180 (pe_size .eq. global_pe_size)
181 case (gs_comm_nvshmem)
185 (pe_size .eq. global_pe_size)
190 end function gs_comm_tunable
215 subroutine gs_tune_select(sel, named)
216 logical,
intent(out) :: sel(:), named(:)
217 character(len=255) :: env_val
218 character(len=32) :: tok, name
219 character(len=1) :: op
220 integer :: env_len, i, j, k
221 logical :: delta, first
223 sel = gs_tune_default
226 call get_environment_variable(
"NEKO_GS_TUNE", env_val, env_len)
227 if (env_len .eq. 0)
return
229 env_len = min(env_len, len(env_val))
234 do while (i .le. env_len)
235 if (scan(env_val(i:i),
', ') .ne. 0)
then
241 do while (j .le. env_len)
242 if (scan(env_val(j:j),
', ') .ne. 0)
exit
245 tok = gs_tune_upcase(env_val(i:j-1))
250 if (tok(1:1) .eq.
'+' .or. tok(1:1) .eq.
'-')
then
258 delta = (op .ne.
' ')
259 if (.not. delta) sel = .false.
261 else if ((op .ne.
' ') .neqv. delta)
then
262 call neko_error(
'NEKO_GS_TUNE: plain backend names and +/- ' // &
263 'prefixed ones cannot be mixed')
266 k = gs_tune_index(name)
268 call neko_error(
'NEKO_GS_TUNE: not a tunable Gather-scatter ' // &
269 'comm. backend: ' // trim(name))
271 sel(k) = (op .ne.
'-')
275 end subroutine gs_tune_select
285 function gs_tune_index(name)
result(idx)
286 character(len=*),
intent(in) :: name
287 integer :: idx, bcknd, i
289 select case (trim(name))
292 case (
'NEIGHBOUR',
'NEIGHBOR')
293 bcknd = gs_comm_neighbour
294 case (
'SHMEM',
'OPENSHMEM')
295 bcknd = gs_comm_openshmem
299 bcknd = gs_comm_utofu
300 case (
'MPIRMA',
'RMA')
301 bcknd = gs_comm_mpirma
303 bcknd = gs_comm_crystal
305 bcknd = gs_comm_mpigpu
306 case (
'NCCL',
'RCCL')
309 bcknd = gs_comm_nvshmem
311 bcknd = gs_comm_crystalgpu
317 do i = 1,
size(gs_tune_bcknd)
318 if (gs_tune_bcknd(i) .eq. bcknd) idx = i
321 end function gs_tune_index
327 function gs_tune_upcase(str)
result(upper)
328 character(len=*),
intent(in) :: str
329 character(len=len(str)) :: upper
334 if (c .ge. iachar(
'a') .and. c .le. iachar(
'z'))
then
335 upper(i:i) = achar(c - (iachar(
'a') - iachar(
'A')))
337 upper(i:i) = str(i:i)
341 end function gs_tune_upcase
356 subroutine gs_comm_switch(gs, comm_bcknd)
357 type(gs_t),
intent(inout) :: gs
358 integer,
intent(in) :: comm_bcknd
359 class(gs_comm_t),
allocatable :: comm_new
361 call gs_comm_alloc(comm_new, comm_bcknd)
362 call comm_new%take_schedule(gs%comm)
365 call move_alloc(comm_new, gs%comm)
366 call gs%comm%init_schedule()
368 gs%bcknd%shared_on_host = .not. gs_comm_on_device(comm_bcknd)
370 end subroutine gs_comm_switch
381 module function gs_time_ops(gs, u, n, op, ntrials) result(t)
382 type(gs_t),
intent(inout) :: gs
383 integer,
intent(in) :: n
384 real(kind=rp),
dimension(n),
intent(inout) :: u
385 integer,
intent(in) :: op, ntrials
389 do i = 1, gs_tune_nwarmup
390 call gs_op_vector(gs, u, n, op)
394 call mpi_barrier(neko_comm)
398 call gs_op_vector(gs, u, n, op)
401 t = (mpi_wtime() - t) /
real(ntrials, dp)
403 end function gs_time_ops
430 module subroutine gs_tune_comm(gs, n, comm_bcknd)
431 type(gs_t),
intent(inout) :: gs
432 integer,
intent(in) :: n
433 integer,
intent(in) :: comm_bcknd
434 character(len=LOG_SIZE) :: log_buf
435 character(len=13) :: label
436 character(len=6) :: strtgy_str
437 integer,
allocatable :: cand(:)
438 real(kind=dp),
allocatable :: cand_time(:)
439 real(kind=rp),
allocatable :: tmp(:)
441 integer :: i, best, nmin, cur, dev_strtgy, dev_strtgy_avg
442 logical :: dev_strtgy_env
444 allocate(cand, source = gs_comm_cand())
458 call mpi_allreduce(mpi_in_place, nmin, 1, mpi_integer, mpi_min, neko_comm)
459 if (nmin .eq. 0 .or.
size(cand) .lt. 2)
then
460 call neko_log%message(
'Comm tuning : skipped')
461 if (nmin .gt. 0 .and.
size(cand) .eq. 1)
then
462 if (cand(1) .ne. cur)
call gs_comm_switch(gs, cand(1))
466 if (cur .eq. gs_comm_mpigpu)
call gs_tune_dev_strtgy(gs, n)
468 call neko_log%message(
'Tuned comm : ' // gs_comm_name(cur))
472 allocate(cand_time(
size(cand)))
478 call device_map(tmp, tmp_d, n)
479 call device_memcpy(tmp, tmp_d, n, host_to_device, sync = .false.)
486 dev_strtgy_env = .false.
489 .not. caf_signal_tuned)
then
492 cand_time(i) = gs_tune_caf_signal(gs, tmp, n)
493 caf_signal_tuned = .true.
495 if (cand(i) .ne. cur)
call gs_comm_switch(gs, cand(i))
496 if (cand(i) .eq. gs_comm_mpigpu)
then
500 call gs_tune_strtgy(gs, tmp, n, dev_strtgy_avg, dev_strtgy_env, &
502 dev_strtgy = gs_get_strtgy(gs)
504 cand_time(i) = gs_time_ops(gs, tmp, n, gs_op_min, &
514 call mpi_allreduce(mpi_in_place, cand_time,
size(cand), &
515 mpi_double_precision, mpi_sum, neko_comm)
516 cand_time = cand_time / pe_size
518 best = minloc(cand_time, 1)
521 label = adjustl(gs_comm_name(cand(i)))
534 if (cand(i) .eq. gs_comm_mpigpu .and. dev_strtgy_avg .ge. 0)
then
535 write(label,
'(A,B0.2,A)')
'Dev. MPI [', dev_strtgy_avg,
']'
538 if (dev_strtgy_env) strtgy_str =
' (env)'
542 write(log_buf,
'(A,A,ES10.3,A,A)') label,
': ', cand_time(i),
' s', &
544 call neko_log%message(log_buf)
547 if (cand(best) .ne. cur)
call gs_comm_switch(gs, cand(best))
551 if (dev_strtgy .ge. 0)
call gs_set_strtgy(gs, dev_strtgy)
553 call neko_log%message(
'Tuned comm : ' // gs_comm_name(cand(best)))
555 deallocate(cand_time)
557 end subroutine gs_tune_comm
569 module subroutine gs_tune_dev_strtgy(gs, n)
570 type(gs_t),
intent(inout) :: gs
571 integer,
intent(in) :: n
572 character(len=LOG_SIZE) :: log_buf
573 real(kind=rp),
allocatable :: tmp(:)
575 integer :: strtgy_avg
581 call device_map(tmp, tmp_d, n)
582 call device_memcpy(tmp, tmp_d, n, host_to_device, sync = .false.)
584 call gs_tune_strtgy(gs, tmp, n, strtgy_avg, from_env)
586 call device_unmap(tmp, tmp_d)
591 write(log_buf,
'(A,B0.2,A)')
'Env. strtgy : [', strtgy_avg,
']'
593 write(log_buf,
'(A,B0.2,A)')
'Avg. strtgy : [', strtgy_avg,
']'
595 call neko_log%message(log_buf)
597 end subroutine gs_tune_dev_strtgy
628 subroutine gs_tune_strtgy(gs, u, n, strtgy_avg, from_env, t)
629 type(gs_t),
intent(inout) :: gs
630 integer,
intent(in) :: n
631 real(kind=rp),
dimension(n),
intent(inout) :: u
632 integer,
intent(out) :: strtgy_avg
633 logical,
intent(out) :: from_env
634 real(kind=dp),
intent(out),
optional :: t
635 integer,
parameter :: strtgy(4) = [int(b
'00'), int(b
'01'), int(b
'10'), &
637 real(kind=dp) :: strtgy_time(
size(strtgy))
638 character(len=255) :: env_strtgy
639 integer :: i, env_len, best, avg
641 call get_environment_variable(
"NEKO_GS_STRTGY", env_strtgy, env_len)
642 from_env = (env_len .gt. 0)
644 if (.not. from_env)
then
645 do i = 1,
size(strtgy)
646 call gs_set_strtgy(gs, strtgy(i))
647 strtgy_time(i) = gs_time_ops(gs, u, n, gs_op_min, gs_tune_ntrials)
650 best = minloc(strtgy_time, 1)
651 call gs_set_strtgy(gs, strtgy(best))
652 if (
present(t)) t = strtgy_time(best)
655 call mpi_allreduce(mpi_in_place, avg, 1, mpi_integer, mpi_sum, &
659 read(env_strtgy(1:env_len), *) best
661 if (best .lt. 1 .or. best .gt.
size(strtgy))
then
662 call neko_error(
'Invalid gs sync strtgy')
665 call gs_set_strtgy(gs, strtgy(best))
667 t = gs_time_ops(gs, u, n, gs_op_min, gs_tune_ntrials)
674 strtgy_avg = strtgy(avg)
676 end subroutine gs_tune_strtgy
683 subroutine gs_set_strtgy(gs, strtgy)
684 type(gs_t),
intent(inout) :: gs
685 integer,
intent(in) :: strtgy
687 select type (c => gs%comm)
688 type is (gs_device_mpi_t)
692 end subroutine gs_set_strtgy
698 function gs_get_strtgy(gs)
result(strtgy)
699 type(gs_t),
intent(in) :: gs
704 select type (c => gs%comm)
705 type is (gs_device_mpi_t)
709 end function gs_get_strtgy
728 function gs_tune_caf_signal(gs, u, n)
result(t)
729 type(gs_t),
intent(inout) :: gs
730 integer,
intent(in) :: n
731 real(kind=rp),
dimension(n),
intent(inout) :: u
733 character(len=LOG_SIZE) :: log_buf
734 character(len=13) :: label
735 integer,
allocatable :: mode(:)
736 real(kind=dp),
allocatable :: mode_time(:)
740 allocate(mode_time(
size(mode)))
746 call gs_comm_switch(gs, gs_comm_caf)
747 mode_time(i) = gs_time_ops(gs, u, n, gs_op_min, gs_tune_ntrials)
750 call mpi_allreduce(mpi_in_place, mode_time,
size(mode), &
751 mpi_double_precision, mpi_sum, neko_comm)
752 mode_time = mode_time / pe_size
754 best = minloc(mode_time, 1)
758 write(log_buf,
'(A,A,ES10.3,A)') label,
': ', mode_time(i),
' s'
759 call neko_log%message(log_buf)
763 if (best .ne.
size(mode))
then
765 call gs_comm_switch(gs, gs_comm_caf)
772 deallocate(mode, mode_time)
774 end function gs_tune_caf_signal
Defines Coarray Fortran gather-scatter communication.
integer function, public gs_caf_mode_get()
The signaling mode currently in force, or 0 if none has been bound yet (no gs_caf_t has been initiali...
integer function, dimension(:), allocatable, public gs_caf_signal_modes()
The signaling modes this build can run, in the order they should be benchmarked. Events are only avai...
logical function, public gs_caf_usable()
Whether the coarray backend can actually run in this job. GS_CAF_AVAIL only says that the compiler ac...
subroutine, public gs_caf_set_mode(mode)
Bind the signaling mode shared by every gs_caf_t instance, allocating whatever module-level state the...
character(len=12) function, public gs_caf_mode_name(mode)
Name of the signaling mode mode, right-adjusted for the log.
logical, parameter, public gs_caf_avail
Whether coarray support was built into this Neko. Lets callers (e.g. the gs comm. autotuner) skip the...
logical function, public gs_caf_signal_auto()
Whether the signaling mode should be selected by benchmarking, i.e. NEKO_GS_CAF_SIGNALING=auto....
Defines NCCL based gather-scatter communication.
logical, parameter, public gs_device_nccl_avail
Whether NCCL (or its ROCm equivalent RCCL) was built into this Neko (–with-nccl / –with-rccl)....
Defines GPU aware MPI gather-scatter communication.
logical, parameter, public gs_device_shmem_avail
Whether NVSHMEM was built into this Neko (–with-nvshmem). Lets callers (e.g. the gs comm....
Defines MPI one-sided (RMA) gather-scatter communication.
logical, parameter, public gs_mpi_rma_avail
MPI RMA needs nothing beyond MPI-3, so the backend is always built. Kept as a parameter for symmetry ...
Defines OpenSHMEM gather-scatter communication.
logical, parameter, public gs_shmem_avail
Whether a native OpenSHMEM library was built into this Neko (–with-openshmem). Lets callers (e....
Defines a gather-scatter backend using the native Tofu interconnect (uTofu). Each rank registers its ...
logical, parameter, public gs_utofu_avail
Whether uTofu support was built into this Neko (–with-utofu). Lets callers (e.g. the gs comm....
integer, parameter neko_bcknd_hip
integer, parameter neko_bcknd_device
logical, parameter neko_device_mpi
integer, parameter neko_bcknd_cuda