Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_tune.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!
34submodule(gather_scatter) gs_tune
35 use gs_shmem, only : gs_shmem_avail
39 use gs_utofu, only : gs_utofu_avail
44 implicit none
45
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, &
54 gs_comm_nvshmem]
55
60 logical, parameter :: GS_TUNE_DEFAULT(11) = [.true., .true., .true., &
61 .true., .false., .true., .true., .true., .true., .true., .false.]
62
68 logical, parameter :: GS_TUNE_DEV_AVAIL = (neko_bcknd_hip .eq. 1) .or. &
69 (neko_bcknd_cuda .eq. 1)
70
76 logical, save :: caf_signal_tuned = .false.
77
78contains
79
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
95
96 call gs_tune_select(sel, named)
97
98 n = 0
99 do i = 1, size(gs_tune_bcknd)
100 if (.not. sel(i)) cycle
101 if (gs_comm_tunable(gs_tune_bcknd(i))) then
102 n = n + 1
103 c(n) = gs_tune_bcknd(i)
104 else if (named(i)) then
105 ! Asked for by name, but this build or run cannot drive it: say so
106 ! rather than leaving the candidate quietly missing from the
107 ! comparison below
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)
111 end if
112 end do
113
114 allocate(cand(n))
115 cand = c(1:n)
116
117 end function gs_comm_cand
118
128 function gs_comm_tunable(comm_bcknd) result(tunable)
129 integer, intent(in) :: comm_bcknd
130 logical :: tunable
131
132 select case (comm_bcknd)
133 case (gs_comm_mpi, gs_comm_neighbour)
134 tunable = .true.
135 case (gs_comm_crystal)
136 ! Nothing beyond MPI-1, and it addresses its partners by their rank
137 ! in NEKO_COMM, so a communicator split does not rule it out either.
138 ! It aggregates the halo into one message per routing stage instead
139 ! of one per peer, which is a win only where per-message overhead
140 ! dominates; the benchmark is what decides that
141 tunable = .true.
142 case (gs_comm_mpirma)
143 ! Nothing beyond MPI-3, and it addresses its peers by their rank in
144 ! NEKO_COMM rather than by a global PE or image number, so unlike the
145 ! other one-sided backends a communicator split (NEKO_COMM_ID) does
146 ! not rule it out. What it does assume is that the implementation
147 ! makes RMA progress without the target entering MPI; on one that
148 ! does not (Open MPI osc/pt2pt) it is slow rather than wrong, so it
149 ! loses the benchmark and is dropped, which is the outcome the
150 ! autotuning exists to produce. Rule it out with NEKO_GS_TUNE=-MPIRMA
151 ! if paying for that measurement is not worth it.
152 tunable = gs_mpi_rma_avail
153 case (gs_comm_openshmem)
154 ! The one-sided backends that address their peers by global PE or
155 ! image number (OpenSHMEM PEs, coarray images) are only correct when
156 ! NEKO_COMM spans every process, so skip them when the run has been
157 ! split into several communicators (NEKO_COMM_ID). uTofu exchanges
158 ! its addresses over NEKO_COMM itself and is unaffected.
159 tunable = gs_shmem_avail .and. (pe_size .eq. global_pe_size)
160 case (gs_comm_caf)
161 ! Coarray support at configure time says nothing about the job
162 ! actually running more than one image, see gs_caf_usable
163 tunable = gs_caf_avail .and. gs_caf_usable() .and. &
164 (pe_size .eq. global_pe_size)
165 case (gs_comm_utofu)
166 tunable = gs_utofu_avail
167 case (gs_comm_mpigpu, gs_comm_crystalgpu)
168 ! Both hand device pointers straight to MPI, which only holds on a
169 ! build configured for device-aware MPI (--enable-device-mpi). They
170 ! address their peers by their rank in NEKO_COMM, so a communicator
171 ! split does not rule them out.
172 tunable = gs_tune_dev_avail .and. neko_device_mpi
173 case (gs_comm_nccl)
174 ! The NCCL communicator is built once at startup
175 ! (neko_comm_nccl_init) over the ranks of NEKO_COMM, but from a
176 ! unique id broadcast over MPI_COMM_WORLD, so it is only well defined
177 ! when NEKO_COMM spans every process, as for the PE addressed
178 ! backends above
179 tunable = gs_tune_dev_avail .and. gs_device_nccl_avail .and. &
180 (pe_size .eq. global_pe_size)
181 case (gs_comm_nvshmem)
182 ! Addresses its peers by NVSHMEM PE, as OpenSHMEM does, so the same
183 ! restriction on communicator splits applies
184 tunable = gs_tune_dev_avail .and. gs_device_shmem_avail .and. &
185 (pe_size .eq. global_pe_size)
186 case default
187 tunable = .false.
188 end select
189
190 end function gs_comm_tunable
191
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
222
223 sel = gs_tune_default
224 named = .false.
225
226 call get_environment_variable("NEKO_GS_TUNE", env_val, env_len)
227 if (env_len .eq. 0) return
228 ! env_len is the length of the value, which may well exceed the buffer
229 env_len = min(env_len, len(env_val))
230
231 delta = .false.
232 first = .true.
233 i = 1
234 do while (i .le. env_len)
235 if (scan(env_val(i:i), ', ') .ne. 0) then
236 i = i + 1
237 cycle
238 end if
239
240 j = i
241 do while (j .le. env_len)
242 if (scan(env_val(j:j), ', ') .ne. 0) exit
243 j = j + 1
244 end do
245 tok = gs_tune_upcase(env_val(i:j-1))
246 i = j
247
248 op = ' '
249 name = tok
250 if (tok(1:1) .eq. '+' .or. tok(1:1) .eq. '-') then
251 op = tok(1:1)
252 name = tok(2:)
253 end if
254
255 if (first) then
256 ! The first name decides how the list is read; a plain one starts
257 ! the set from nothing rather than from the default
258 delta = (op .ne. ' ')
259 if (.not. delta) sel = .false.
260 first = .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')
264 end if
265
266 k = gs_tune_index(name)
267 if (k .eq. 0) then
268 call neko_error('NEKO_GS_TUNE: not a tunable Gather-scatter ' // &
269 'comm. backend: ' // trim(name))
270 end if
271 sel(k) = (op .ne. '-')
272 named(k) = .true.
273 end do
274
275 end subroutine gs_tune_select
276
285 function gs_tune_index(name) result(idx)
286 character(len=*), intent(in) :: name
287 integer :: idx, bcknd, i
288
289 select case (trim(name))
290 case ('MPI')
291 bcknd = gs_comm_mpi
292 case ('NEIGHBOUR', 'NEIGHBOR')
293 bcknd = gs_comm_neighbour
294 case ('SHMEM', 'OPENSHMEM')
295 bcknd = gs_comm_openshmem
296 case ('CAF')
297 bcknd = gs_comm_caf
298 case ('UTOFU')
299 bcknd = gs_comm_utofu
300 case ('MPIRMA', 'RMA')
301 bcknd = gs_comm_mpirma
302 case ('CRYSTAL')
303 bcknd = gs_comm_crystal
304 case ('MPIGPU')
305 bcknd = gs_comm_mpigpu
306 case ('NCCL', 'RCCL')
307 bcknd = gs_comm_nccl
308 case ('NVSHMEM')
309 bcknd = gs_comm_nvshmem
310 case ('CRYSTALGPU')
311 bcknd = gs_comm_crystalgpu
312 case default
313 bcknd = 0
314 end select
315
316 idx = 0
317 do i = 1, size(gs_tune_bcknd)
318 if (gs_tune_bcknd(i) .eq. bcknd) idx = i
319 end do
320
321 end function gs_tune_index
322
327 function gs_tune_upcase(str) result(upper)
328 character(len=*), intent(in) :: str
329 character(len=len(str)) :: upper
330 integer :: i, c
331
332 do i = 1, len(str)
333 c = iachar(str(i:i))
334 if (c .ge. iachar('a') .and. c .le. iachar('z')) then
335 upper(i:i) = achar(c - (iachar('a') - iachar('A')))
336 else
337 upper(i:i) = str(i:i)
338 end if
339 end do
340
341 end function gs_tune_upcase
342
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
360
361 call gs_comm_alloc(comm_new, comm_bcknd)
362 call comm_new%take_schedule(gs%comm)
363 call gs%comm%free()
364 deallocate(gs%comm)
365 call move_alloc(comm_new, gs%comm)
366 call gs%comm%init_schedule()
367
368 gs%bcknd%shared_on_host = .not. gs_comm_on_device(comm_bcknd)
369
370 end subroutine gs_comm_switch
371
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
386 real(kind=dp) :: t
387 integer :: i
388
389 do i = 1, gs_tune_nwarmup
390 call gs_op_vector(gs, u, n, op)
391 end do
392
393 if (neko_bcknd_device .eq. 1) call device_sync
394 call mpi_barrier(neko_comm)
395
396 t = mpi_wtime()
397 do i = 1, ntrials
398 call gs_op_vector(gs, u, n, op)
399 end do
400 if (neko_bcknd_device .eq. 1) call device_sync
401 t = (mpi_wtime() - t) / real(ntrials, dp)
402
403 end function gs_time_ops
404
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(:)
440 type(c_ptr) :: tmp_d
441 integer :: i, best, nmin, cur, dev_strtgy, dev_strtgy_avg
442 logical :: dev_strtgy_env
443
444 allocate(cand, source = gs_comm_cand())
445
446 ! Track what gs is running as the sweep proceeds. The candidate list
447 ! need neither start with the backend the schedule was built with nor
448 ! contain it at all, and switching is what costs, so every candidate is
449 ! switched to exactly once
450 cur = comm_bcknd
451
452 ! A rank without any dofs skips the halo exchange in gs_op_vector, which
453 ! would leave every other rank hanging in a collective based backend.
454 ! Keep the current backend in that case. NEKO_GS_TUNE may also have left
455 ! us with nothing to compare, in which case a lone candidate is simply
456 ! switched to, as if it had been requested with NEKO_GS_COMM.
457 nmin = n
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))
463 cur = cand(1)
464 ! Nothing to compare the backend against, but device MPI still has
465 ! a strategy to pick
466 if (cur .eq. gs_comm_mpigpu) call gs_tune_dev_strtgy(gs, n)
467 end if
468 call neko_log%message('Tuned comm : ' // gs_comm_name(cur))
469 return
470 end if
471
472 allocate(cand_time(size(cand)))
473
474 tmp_d = c_null_ptr
475 allocate(tmp(n))
476 tmp = 1.0_rp
477 if (neko_bcknd_device .eq. 1) then
478 call device_map(tmp, tmp_d, n)
479 call device_memcpy(tmp, tmp_d, n, host_to_device, sync = .false.)
480 end if
481
482 ! GS_OP_MIN leaves the working vector untouched, so the benchmark can
483 ! run for any number of trials without the values growing out of range
484 dev_strtgy = -1
485 dev_strtgy_avg = -1
486 dev_strtgy_env = .false.
487 do i = 1, size(cand)
488 if (cand(i) .eq. gs_comm_caf .and. gs_caf_signal_auto() .and. &
489 .not. caf_signal_tuned) then
490 ! Benchmark the signaling modes as well; this leaves the CAF
491 ! backend allocated in the winning mode
492 cand_time(i) = gs_tune_caf_signal(gs, tmp, n)
493 caf_signal_tuned = .true.
494 else
495 if (cand(i) .ne. cur) call gs_comm_switch(gs, cand(i))
496 if (cand(i) .eq. gs_comm_mpigpu) then
497 ! Benchmark the synchronisation strategies as well; this leaves
498 ! the fastest one bound, and it has to be remembered in case the
499 ! sweep switches away from device MPI and back again
500 call gs_tune_strtgy(gs, tmp, n, dev_strtgy_avg, dev_strtgy_env, &
501 cand_time(i))
502 dev_strtgy = gs_get_strtgy(gs)
503 else
504 cand_time(i) = gs_time_ops(gs, tmp, n, gs_op_min, &
505 gs_tune_ntrials)
506 end if
507 end if
508 cur = cand(i)
509 end do
510
511 if (neko_bcknd_device .eq. 1) call device_unmap(tmp, tmp_d)
512 deallocate(tmp)
513
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
517
518 best = minloc(cand_time, 1)
519
520 do i = 1, size(cand)
521 label = adjustl(gs_comm_name(cand(i)))
522 ! What the coarray backend costs depends on the signaling mode in
523 ! force, so report the timing under the mode it was measured in
524 if (cand(i) .eq. gs_comm_caf .and. gs_caf_mode_get() .ne. 0) then
525 label = 'CAF (' // &
526 trim(adjustl(gs_caf_mode_name(gs_caf_mode_get()))) // ')'
527 end if
528 ! Device MPI is likewise only as fast as the synchronisation strategy
529 ! it ran under, and that strategy is what the run goes on to use, so
530 ! name it on the line it was measured on rather than above the
531 ! comparison. 'Dev. MPI [xx]' is exactly the width of the label
532 ! field, so the column is unaffected
533 strtgy_str = ''
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, ']'
536 ! A strategy that was named rather than measured is not a property
537 ! of this comparison, so it is flagged instead of quietly shown
538 if (dev_strtgy_env) strtgy_str = ' (env)'
539 end if
540 ! ES10.3 + ' s' fills the same 12 columns as the right-adjusted
541 ! backend names, so the unit lines up with their last letter
542 write(log_buf, '(A,A,ES10.3,A,A)') label, ': ', cand_time(i), ' s', &
543 trim(strtgy_str)
544 call neko_log%message(log_buf)
545 end do
546
547 if (cand(best) .ne. cur) call gs_comm_switch(gs, cand(best))
548
549 ! A switch back to device MPI allocates a fresh backend, which comes up
550 ! with the default strategy rather than the one benchmarked above
551 if (dev_strtgy .ge. 0) call gs_set_strtgy(gs, dev_strtgy)
552
553 call neko_log%message('Tuned comm : ' // gs_comm_name(cand(best)))
554
555 deallocate(cand_time)
556
557 end subroutine gs_tune_comm
558
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(:)
574 type(c_ptr) :: tmp_d
575 integer :: strtgy_avg
576 logical :: from_env
577
578 tmp_d = c_null_ptr
579 allocate(tmp(n))
580 tmp = 1.0_rp
581 call device_map(tmp, tmp_d, n)
582 call device_memcpy(tmp, tmp_d, n, host_to_device, sync = .false.)
583
584 call gs_tune_strtgy(gs, tmp, n, strtgy_avg, from_env)
585
586 call device_unmap(tmp, tmp_d)
587 deallocate(tmp)
588
589 ! Nothing else was tuned, so the strategy gets the line to itself
590 if (from_env) then
591 write(log_buf, '(A,B0.2,A)') 'Env. strtgy : [', strtgy_avg, ']'
592 else
593 write(log_buf, '(A,B0.2,A)') 'Avg. strtgy : [', strtgy_avg, ']'
594 end if
595 call neko_log%message(log_buf)
596
597 end subroutine gs_tune_dev_strtgy
598
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'), &
636 int(b'11')]
637 real(kind=dp) :: strtgy_time(size(strtgy))
638 character(len=255) :: env_strtgy
639 integer :: i, env_len, best, avg
640
641 call get_environment_variable("NEKO_GS_STRTGY", env_strtgy, env_len)
642 from_env = (env_len .gt. 0)
643
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)
648 end do
649
650 best = minloc(strtgy_time, 1)
651 call gs_set_strtgy(gs, strtgy(best))
652 if (present(t)) t = strtgy_time(best)
653
654 avg = best
655 call mpi_allreduce(mpi_in_place, avg, 1, mpi_integer, mpi_sum, &
656 neko_comm)
657 avg = avg / pe_size
658 else
659 read(env_strtgy(1:env_len), *) best
660
661 if (best .lt. 1 .or. best .gt. size(strtgy)) then
662 call neko_error('Invalid gs sync strtgy')
663 end if
664
665 call gs_set_strtgy(gs, strtgy(best))
666 if (present(t)) then
667 t = gs_time_ops(gs, u, n, gs_op_min, gs_tune_ntrials)
668 end if
669
670 ! Every rank was given the same one, so there is nothing to average
671 avg = best
672 end if
673
674 strtgy_avg = strtgy(avg)
675
676 end subroutine gs_tune_strtgy
677
683 subroutine gs_set_strtgy(gs, strtgy)
684 type(gs_t), intent(inout) :: gs
685 integer, intent(in) :: strtgy
686
687 select type (c => gs%comm)
688 type is (gs_device_mpi_t)
689 c%nb_strtgy = strtgy
690 end select
691
692 end subroutine gs_set_strtgy
693
698 function gs_get_strtgy(gs) result(strtgy)
699 type(gs_t), intent(in) :: gs
700 integer :: strtgy
701
702 strtgy = -1
703
704 select type (c => gs%comm)
705 type is (gs_device_mpi_t)
706 strtgy = c%nb_strtgy
707 end select
708
709 end function gs_get_strtgy
710
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
732 real(kind=dp) :: t
733 character(len=LOG_SIZE) :: log_buf
734 character(len=13) :: label
735 integer, allocatable :: mode(:)
736 real(kind=dp), allocatable :: mode_time(:)
737 integer :: i, best
738
739 allocate(mode, source = gs_caf_signal_modes())
740 allocate(mode_time(size(mode)))
741
742 do i = 1, size(mode)
743 ! Bind the mode before building the backend: gs_caf_init sets up its
744 ! per-instance signaling state according to the mode in force
745 call gs_caf_set_mode(mode(i))
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)
748 end do
749
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
753
754 best = minloc(mode_time, 1)
755
756 do i = 1, size(mode)
757 label = 'CAF ' // adjustl(gs_caf_mode_name(mode(i)))
758 write(log_buf, '(A,A,ES10.3,A)') label, ': ', mode_time(i), ' s'
759 call neko_log%message(log_buf)
760 end do
761
762 ! The last mode is the one currently allocated
763 if (best .ne. size(mode)) then
764 call gs_caf_set_mode(mode(best))
765 call gs_comm_switch(gs, gs_comm_caf)
766 end if
767
768 call neko_log%message('Tuned CAF sig: ' // gs_caf_mode_name(mode(best)))
769
770 t = mode_time(best)
771
772 deallocate(mode, mode_time)
773
774 end function gs_tune_caf_signal
775
776end submodule gs_tune
double real
Gather-scatter.
Defines Coarray Fortran gather-scatter communication.
Definition gs_caf.F90:34
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...
Definition gs_caf.F90:270
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...
Definition gs_caf.F90:250
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...
Definition gs_caf.F90:219
subroutine, public gs_caf_set_mode(mode)
Bind the signaling mode shared by every gs_caf_t instance, allocating whatever module-level state the...
Definition gs_caf.F90:309
character(len=12) function, public gs_caf_mode_name(mode)
Name of the signaling mode mode, right-adjusted for the log.
Definition gs_caf.F90:283
logical, parameter, public gs_caf_avail
Whether coarray support was built into this Neko. Lets callers (e.g. the gs comm. autotuner) skip the...
Definition gs_caf.F90:57
logical function, public gs_caf_signal_auto()
Whether the signaling mode should be selected by benchmarking, i.e. NEKO_GS_CAF_SIGNALING=auto....
Definition gs_caf.F90:235
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.
Definition gs_shmem.F90:34
logical, parameter, public gs_shmem_avail
Whether a native OpenSHMEM library was built into this Neko (–with-openshmem). Lets callers (e....
Definition gs_shmem.F90:68
Defines a gather-scatter backend using the native Tofu interconnect (uTofu). Each rank registers its ...
Definition gs_utofu.F90:43
logical, parameter, public gs_utofu_avail
Whether uTofu support was built into this Neko (–with-utofu). Lets callers (e.g. the gs comm....
Definition gs_utofu.F90:65
Build configurations.
integer, parameter neko_bcknd_hip
integer, parameter neko_bcknd_device
logical, parameter neko_device_mpi
integer, parameter neko_bcknd_cuda