45 integer,
parameter :: GS_TUNE_BCKND(5) = [gs_comm_mpi, gs_comm_neighbour, &
46 gs_comm_openshmem, gs_comm_caf, gs_comm_utofu]
51 logical,
parameter :: GS_TUNE_DEFAULT(5) = [.true., .true., .true., &
59 logical,
save :: caf_signal_tuned = .false.
72 function gs_comm_host_cand()
result(cand)
73 integer,
allocatable :: cand(:)
74 character(len=LOG_SIZE) :: log_buf
75 character(len=13) :: label
76 logical :: sel(size(GS_TUNE_BCKND)), named(size(GS_TUNE_BCKND))
77 integer :: c(size(GS_TUNE_BCKND)), i, n
79 call gs_tune_select(sel, named)
82 do i = 1,
size(gs_tune_bcknd)
83 if (.not. sel(i)) cycle
84 if (gs_comm_tunable(gs_tune_bcknd(i)))
then
86 c(n) = gs_tune_bcknd(i)
87 else if (named(i))
then
91 label = adjustl(gs_comm_name(gs_tune_bcknd(i)))
92 write(log_buf,
'(A,A,A12)') label,
': ',
'unavailable'
93 call neko_log%message(log_buf)
100 end function gs_comm_host_cand
109 function gs_comm_tunable(comm_bcknd)
result(tunable)
110 integer,
intent(in) :: comm_bcknd
113 select case (comm_bcknd)
114 case (gs_comm_mpi, gs_comm_neighbour)
116 case (gs_comm_openshmem)
127 (pe_size .eq. global_pe_size)
134 end function gs_comm_tunable
157 subroutine gs_tune_select(sel, named)
158 logical,
intent(out) :: sel(:), named(:)
159 character(len=255) :: env_val
160 character(len=32) :: tok, name
161 character(len=1) :: op
162 integer :: env_len, i, j, k
163 logical :: delta, first
165 sel = gs_tune_default
168 call get_environment_variable(
"NEKO_GS_TUNE", env_val, env_len)
169 if (env_len .eq. 0)
return
171 env_len = min(env_len, len(env_val))
176 do while (i .le. env_len)
177 if (scan(env_val(i:i),
', ') .ne. 0)
then
183 do while (j .le. env_len)
184 if (scan(env_val(j:j),
', ') .ne. 0)
exit
187 tok = gs_tune_upcase(env_val(i:j-1))
192 if (tok(1:1) .eq.
'+' .or. tok(1:1) .eq.
'-')
then
200 delta = (op .ne.
' ')
201 if (.not. delta) sel = .false.
203 else if ((op .ne.
' ') .neqv. delta)
then
204 call neko_error(
'NEKO_GS_TUNE: plain backend names and +/- ' // &
205 'prefixed ones cannot be mixed')
208 k = gs_tune_index(name)
210 call neko_error(
'NEKO_GS_TUNE: not a tunable Gather-scatter ' // &
211 'comm. backend: ' // trim(name))
213 sel(k) = (op .ne.
'-')
217 end subroutine gs_tune_select
225 function gs_tune_index(name)
result(idx)
226 character(len=*),
intent(in) :: name
227 integer :: idx, bcknd, i
229 select case (trim(name))
232 case (
'NEIGHBOUR',
'NEIGHBOR')
233 bcknd = gs_comm_neighbour
234 case (
'SHMEM',
'OPENSHMEM')
235 bcknd = gs_comm_openshmem
239 bcknd = gs_comm_utofu
245 do i = 1,
size(gs_tune_bcknd)
246 if (gs_tune_bcknd(i) .eq. bcknd) idx = i
249 end function gs_tune_index
255 function gs_tune_upcase(str)
result(upper)
256 character(len=*),
intent(in) :: str
257 character(len=len(str)) :: upper
262 if (c .ge. iachar(
'a') .and. c .le. iachar(
'z'))
then
263 upper(i:i) = achar(c - (iachar(
'a') - iachar(
'A')))
265 upper(i:i) = str(i:i)
269 end function gs_tune_upcase
280 subroutine gs_comm_switch(gs, comm_bcknd)
281 type(gs_t),
intent(inout) :: gs
282 integer,
intent(in) :: comm_bcknd
283 class(gs_comm_t),
allocatable :: comm_new
285 call gs_comm_alloc(comm_new, comm_bcknd)
286 call comm_new%take_schedule(gs%comm)
289 call move_alloc(comm_new, gs%comm)
290 call gs%comm%init_schedule()
292 end subroutine gs_comm_switch
303 module function gs_time_ops(gs, u, n, op, ntrials) result(t)
304 type(gs_t),
intent(inout) :: gs
305 integer,
intent(in) :: n
306 real(kind=rp),
dimension(n),
intent(inout) :: u
307 integer,
intent(in) :: op, ntrials
311 do i = 1, gs_tune_nwarmup
312 call gs_op_vector(gs, u, n, op)
315 if (neko_bcknd_device .eq. 1)
call device_sync
316 call mpi_barrier(neko_comm)
320 call gs_op_vector(gs, u, n, op)
322 if (neko_bcknd_device .eq. 1)
call device_sync
323 t = (mpi_wtime() - t) /
real(ntrials, dp)
325 end function gs_time_ops
348 module subroutine gs_tune_comm(gs, n, comm_bcknd)
349 type(gs_t),
intent(inout) :: gs
350 integer,
intent(in) :: n
351 integer,
intent(in) :: comm_bcknd
352 character(len=LOG_SIZE) :: log_buf
353 character(len=13) :: label
354 integer,
allocatable :: cand(:)
355 real(kind=dp),
allocatable :: cand_time(:)
356 real(kind=rp),
allocatable :: tmp(:)
358 integer :: i, best, nmin, cur
360 allocate(cand, source = gs_comm_host_cand())
374 call mpi_allreduce(mpi_in_place, nmin, 1, mpi_integer, mpi_min, neko_comm)
375 if (nmin .eq. 0 .or.
size(cand) .lt. 2)
then
376 call neko_log%message(
'Comm tuning : skipped')
377 if (nmin .gt. 0 .and.
size(cand) .eq. 1)
then
378 if (cand(1) .ne. cur)
call gs_comm_switch(gs, cand(1))
381 call neko_log%message(
'Tuned comm : ' // gs_comm_name(cur))
385 allocate(cand_time(
size(cand)))
390 if (neko_bcknd_device .eq. 1)
then
391 call device_map(tmp, tmp_d, n)
392 call device_memcpy(tmp, tmp_d, n, host_to_device, sync = .false.)
399 .not. caf_signal_tuned)
then
402 cand_time(i) = gs_tune_caf_signal(gs, tmp, n)
403 caf_signal_tuned = .true.
405 if (cand(i) .ne. cur)
call gs_comm_switch(gs, cand(i))
406 cand_time(i) = gs_time_ops(gs, tmp, n, gs_op_min, gs_tune_ntrials)
411 if (neko_bcknd_device .eq. 1)
call device_unmap(tmp, tmp_d)
414 call mpi_allreduce(mpi_in_place, cand_time,
size(cand), &
415 mpi_double_precision, mpi_sum, neko_comm)
416 cand_time = cand_time / pe_size
418 best = minloc(cand_time, 1)
421 label = adjustl(gs_comm_name(cand(i)))
430 write(log_buf,
'(A,A,ES10.3,A)') label,
': ', cand_time(i),
' s'
431 call neko_log%message(log_buf)
434 if (cand(best) .ne. cur)
call gs_comm_switch(gs, cand(best))
436 call neko_log%message(
'Tuned comm : ' // gs_comm_name(cand(best)))
438 deallocate(cand_time)
440 end subroutine gs_tune_comm
459 function gs_tune_caf_signal(gs, u, n)
result(t)
460 type(gs_t),
intent(inout) :: gs
461 integer,
intent(in) :: n
462 real(kind=rp),
dimension(n),
intent(inout) :: u
464 character(len=LOG_SIZE) :: log_buf
465 character(len=13) :: label
466 integer,
allocatable :: mode(:)
467 real(kind=dp),
allocatable :: mode_time(:)
471 allocate(mode_time(
size(mode)))
477 call gs_comm_switch(gs, gs_comm_caf)
478 mode_time(i) = gs_time_ops(gs, u, n, gs_op_min, gs_tune_ntrials)
481 call mpi_allreduce(mpi_in_place, mode_time,
size(mode), &
482 mpi_double_precision, mpi_sum, neko_comm)
483 mode_time = mode_time / pe_size
485 best = minloc(mode_time, 1)
489 write(log_buf,
'(A,A,ES10.3,A)') label,
': ', mode_time(i),
' s'
490 call neko_log%message(log_buf)
494 if (best .ne.
size(mode))
then
496 call gs_comm_switch(gs, gs_comm_caf)
503 deallocate(mode, mode_time)
505 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 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....