Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_caf.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!
34module gs_caf
35 use num_types, only : rp
36 use gs_comm, only : gs_comm_t, gs_vec_nc
38 use stack, only : stack_i4_t
39 use comm, only : pe_size
40 use, intrinsic :: iso_c_binding
41#ifdef HAVE_COARRAY_EVENTS
42 use, intrinsic :: iso_fortran_env, only : atomic_int_kind, event_type
43#else
44 use, intrinsic :: iso_fortran_env, only : atomic_int_kind
45#endif
46 use utils, only : neko_error
47 !$ use omp_lib
48 implicit none
49 private
50
54#ifdef HAVE_COARRAY
55 logical, parameter, public :: GS_CAF_AVAIL = .true.
56#else
57 logical, parameter, public :: gs_caf_avail = .false.
58#endif
59
60 ! Signaling mode constants. Selected at first init via the
61 ! NEKO_GS_CAF_SIGNALING environment variable
62 ! ("sync", "atomic", or "event").
63 integer, parameter, public :: gs_caf_signal_sync = 1
64 integer, parameter, public :: gs_caf_signal_atomic = 2
65 integer, parameter, public :: gs_caf_signal_event = 3
66
67#ifdef HAVE_COARRAY
68 ! Module-level receive coarray, shared by all gs_caf_t instances.
69 ! F2008 forbids a derived type from adding a coarray ultimate
70 ! component when its parent type has none, so the coarray buffer is
71 ! held at module scope rather than as a component of gs_caf_t.
72 !
73 ! The buffer is double-buffered: it is allocated to twice the global
74 ! max receive count so that consecutive rounds write to alternating
75 ! halves. In sync mode this eliminates back-pressure entirely (the
76 ! receiver may still be unpacking the previous round, but from a
77 ! different half, so no overwrite hazard exists). In atomic and event
78 ! modes the same property is used to relax the back-pressure spin to
79 ! a one-round tolerance -- the next overwrite is two rounds away, so
80 ! the receiver only needs to be at most one round behind.
81 ! gs_caf_buf_size is the size of one half.
82 !
83 ! Multiple gs_caf_t instances may coexist (each carrying its own
84 ! offset bookkeeping) provided they are used strictly sequentially
85 ! -- no overlapping nbsend/nbwait rounds across instances. The buffer
86 ! is grown on demand to fit the largest gs ever initialised; it is
87 ! never shrunk and is retained for the program lifetime.
88 real(kind=rp), allocatable :: gs_caf_recv_buf(:)[:]
89 integer :: gs_caf_buf_size = 0
90
91 ! Double-buffer parity (0 or 1) per remote rank, flipped at the end of
92 ! every nbwait for the peers that round involved: the round writes to and
93 ! reads from the gs_caf_peer_parity(peer)*gs_caf_buf_size half, the next
94 ! round with that same peer uses the other one.
95 !
96 ! The parity belongs to the buffer, which is module state shared by every
97 ! instance, so it cannot live in gs_caf_t. It has to be per *pair* rather
98 ! than one counter for the whole buffer, because what makes it safe for a
99 ! sender to run ahead of a receiver is "the next round I exchange with this
100 ! peer writes the other half", and only traffic between that pair advances
101 ! it. A single global parity holds only while every instance shares one
102 ! peer set: with instances whose peer sets differ -- hsmg, where the
103 ! Schwarz smoother's gs lives on the extended lx+2 dofmap and so does not
104 ! have the connectivity of the level's own gs -- two consecutive rounds
105 ! between I and J can sit an even number of global rounds apart and land on
106 ! the same half. Nothing then orders I's put against J's still-running
107 ! unpack: in sync mode the intervening round does not name the pair in its
108 ! sync images set, and in atomic mode the back-pressure spin explicitly
109 ! tolerates a receiver one round behind. p-multigrid never shows it, since
110 ! every level shares the partition's peer set and so every pair
111 ! synchronises every round.
112 !
113 ! Sender and receiver stay in step because gs schedules are symmetric: both
114 ! endpoints of a pair take part in the same rounds and flip at the same
115 ! point, at the end of nbwait, so a round's put and its unpack agree on the
116 ! half. Flipping in nbsend as well would make the two ends disagree.
117 integer, allocatable :: gs_caf_peer_parity(:)
118
119 ! Active signaling mode; bound on the first gs_caf_t init from the
120 ! NEKO_GS_CAF_SIGNALING environment variable. Subsequent instances
121 ! must use the same mode (the env var is read once).
122 integer :: gs_caf_mode = 0
123
124 ! Atomic-mode signaling counters, indexed by remote rank.
125 ! gs_caf_data_ready(s_rank) on image r counts rounds image s has put
126 ! into r so far. gs_caf_buf_ready(r_rank) on image s counts rounds
127 ! image r has finished unpacking from s so far. Allocated only in
128 ! atomic mode and shared by all instances.
129 integer(kind=atomic_int_kind), allocatable :: gs_caf_data_ready(:)[:]
130 integer(kind=atomic_int_kind), allocatable :: gs_caf_buf_ready(:)[:]
131
132 ! Local caches of "rounds we have sent to / received from each remote
133 ! rank" -- size pe_size per image, indexed by remote rank. Updated
134 ! locally on every atomic_define / wait completion in nbsend / nbwait.
135 ! Reading these to baseline a new gs_caf_t avoids any remote
136 ! atomic_ref during init, which Cray CCE has historically deadlocked
137 ! on. The values match the remote atomic counters at quiescent
138 ! points (i.e. between gs ops) for symmetric, lockstep gs traffic.
139 integer, allocatable :: gs_caf_send_count(:)
140 integer, allocatable :: gs_caf_recv_count(:)
141
142#ifdef HAVE_COARRAY_EVENTS
143 ! Event-mode signaling. The data_ready event accumulates one post per
144 ! sender per round; buf_ready is the back-channel from receiver to
145 ! sender. Events are scalar coarrays whose count cannot distinguish
146 ! posts coming from different gs_caf_t instances, so event mode is
147 ! restricted to a single live instance at a time.
148 !
149 ! The events are allocatable rather than static module-scope coarrays
150 ! because Cray CCE has historically had layout issues with mixing
151 ! module-scope static coarrays of derived type with allocatable
152 ! coarrays on the symmetric heap; an explicit allocate side-steps
153 ! that.
154 type(event_type), allocatable :: gs_caf_data_ready_ev[:]
155 type(event_type), allocatable :: gs_caf_buf_ready_ev[:]
156 logical :: gs_caf_event_in_use = .false.
157#endif
158#endif
159
171 type, public, extends(gs_comm_t) :: gs_caf_t
173 real(kind=rp), allocatable :: send_buf(:)
175 integer, allocatable :: send_len(:), recv_len(:)
177 integer, allocatable :: send_offset(:), recv_offset(:)
179 integer, allocatable :: dest_offset(:)
181 integer, allocatable :: send_img(:), recv_img(:)
185 integer, allocatable :: peer(:)
189 integer, allocatable :: sync_img(:)
192 logical :: send_started = .false.
193 contains
194 procedure, pass(this) :: init => gs_caf_init
195 procedure, pass(this) :: free => gs_caf_free
196 procedure, pass(this) :: nbsend => gs_nbsend_caf
197 procedure, pass(this) :: nbrecv => gs_nbrecv_caf
198 procedure, pass(this) :: nbwait => gs_nbwait_caf
199 procedure, pass(this) :: nbsend_vec => gs_nbsend_vec_caf
200 procedure, pass(this) :: nbrecv_vec => gs_nbrecv_vec_caf
201 procedure, pass(this) :: nbwait_vec => gs_nbwait_vec_caf
202 end type gs_caf_t
203
206
207contains
208
218 function gs_caf_usable() result(usable)
219 logical :: usable
220
221#ifdef HAVE_COARRAY
222 usable = (num_images() .eq. pe_size)
223#else
224 usable = .false.
225#endif
226
227 end function gs_caf_usable
228
234 function gs_caf_signal_auto() result(auto)
235 logical :: auto
236 character(len=64) :: env_val
237 integer :: env_len
238
239 call get_environment_variable("NEKO_GS_CAF_SIGNALING", env_val, env_len)
240 auto = (env_len .gt. 0)
241 if (auto) auto = (env_val(1:env_len) .eq. "auto")
242
243 end function gs_caf_signal_auto
244
249 function gs_caf_signal_modes() result(mode)
250 integer, allocatable :: mode(:)
251 integer :: m(3), n
252
253 n = 2
254 m(1) = gs_caf_signal_sync
256#ifdef HAVE_COARRAY_EVENTS
257 n = n + 1
259#endif
260
261 allocate(mode(n))
262 mode = m(1:n)
263
264 end function gs_caf_signal_modes
265
269 function gs_caf_mode_get() result(mode)
270 integer :: mode
271
272#ifdef HAVE_COARRAY
273 mode = gs_caf_mode
274#else
275 mode = 0
276#endif
277
278 end function gs_caf_mode_get
279
282 function gs_caf_mode_name(mode) result(name)
283 integer, intent(in) :: mode
284 character(len=12) :: name
285
286 select case (mode)
287 case (gs_caf_signal_sync)
288 name = ' sync'
290 name = ' atomic'
292 name = ' event'
293 case default
294 name = ' unknown'
295 call neko_error('Unknown coarray gather-scatter signaling mode')
296 end select
297
298 end function gs_caf_mode_name
299
308 subroutine gs_caf_set_mode(mode)
309 integer, intent(in) :: mode
310#ifdef HAVE_COARRAY
311 integer :: i
312
313 select case (mode)
314 case (gs_caf_signal_sync)
315 ! Nothing to set up; the image set is built per instance in init
317 if (.not. allocated(gs_caf_data_ready)) then
318 allocate(gs_caf_data_ready(0:pe_size - 1)[*])
319 allocate(gs_caf_buf_ready(0:pe_size - 1)[*])
320 allocate(gs_caf_send_count(0:pe_size - 1))
321 allocate(gs_caf_recv_count(0:pe_size - 1))
322 gs_caf_send_count = 0
323 gs_caf_recv_count = 0
324 ! F2008 forbids mixing atomic and non-atomic accesses on the
325 ! same variable, so initialise via atomic_define rather than
326 ! a regular array assignment.
327 do i = 0, pe_size - 1
328 call atomic_define(gs_caf_data_ready(i), 0_atomic_int_kind)
329 call atomic_define(gs_caf_buf_ready(i), 0_atomic_int_kind)
330 end do
331 end if
333#ifdef HAVE_COARRAY_EVENTS
334 ! One set of event coarrays shared by all instances. The guard
335 ! against overlapping gs ops is enforced in nbsend/nbwait, not here.
336 if (.not. allocated(gs_caf_data_ready_ev)) then
337 allocate(gs_caf_data_ready_ev[*])
338 allocate(gs_caf_buf_ready_ev[*])
339 end if
340#else
341 call neko_error("NEKO_GS_CAF_SIGNALING=event requires a Fortran " // &
342 "compiler with coarray events support")
343#endif
344 case default
345 call neko_error('Unknown coarray gather-scatter signaling mode')
346 end select
347
348 gs_caf_mode = mode
349#else
350 call neko_error("Coarray Fortran support not built; reconfigure with " // &
351 "a coarray-capable Fortran compiler")
352#endif
353 end subroutine gs_caf_set_mode
354
356 subroutine gs_caf_init(this, send_pe, recv_pe)
357 class(gs_caf_t), intent(inout) :: this
358 type(stack_i4_t), intent(inout) :: send_pe
359 type(stack_i4_t), intent(inout) :: recv_pe
360#ifdef HAVE_COARRAY
361 integer, allocatable :: dest_xchg(:)[:]
362 logical, allocatable :: in_neigh(:)
363 integer :: i, nsend, nrecv, send_total, recv_total, max_total, n_neigh
364 integer :: me, env_len
365 character(len=64) :: env_val
366
367 ! A build with coarray support is not necessarily a build that can run
368 ! them across the job (see gs_caf_usable). Catch it here rather than
369 ! silently exchanging nothing, or deadlocking in the first put.
370 if (.not. gs_caf_usable()) then
371 call neko_error("Coarray gather-scatter needs one image per rank; " // &
372 "this build or run has num_images() /= pe_size")
373 end if
374
375 ! Bind the signaling mode on the first init. With
376 ! NEKO_GS_CAF_SIGNALING=auto the mode is instead selected by
377 ! benchmarking, and the gs comm. autotuner has already bound it with
378 ! gs_caf_set_mode by the time we get here; falling through to sync
379 ! covers the case where nothing tuned it (CAF requested explicitly).
380 if (gs_caf_mode .eq. 0) then
381 call get_environment_variable("NEKO_GS_CAF_SIGNALING", env_val, env_len)
382 if (env_len .gt. 0 .and. env_val(1:env_len) .eq. "atomic") then
384 else if (env_len .gt. 0 .and. env_val(1:env_len) .eq. "event") then
386 else
388 end if
389 end if
390
391 call this%init_order(send_pe, recv_pe)
392
393 nsend = size(this%send_pe)
394 nrecv = size(this%recv_pe)
395
396 allocate(this%send_len(nsend), this%send_offset(nsend), &
397 this%send_img(nsend), this%dest_offset(nsend))
398 allocate(this%recv_len(nrecv), this%recv_offset(nrecv), &
399 this%recv_img(nrecv))
400
401 ! Local receive layout
402 recv_total = 0
403 do i = 1, nrecv
404 this%recv_len(i) = this%recv_dof(this%recv_pe(i))%size()
405 this%recv_offset(i) = recv_total
406 recv_total = recv_total + this%recv_len(i)
407 this%recv_img(i) = this%recv_pe(i) + 1
408 end do
409
410 ! Local send layout (concatenated per-peer slabs in one buffer)
411 send_total = 0
412 do i = 1, nsend
413 this%send_len(i) = this%send_dof(this%send_pe(i))%size()
414 this%send_offset(i) = send_total
415 send_total = send_total + this%send_len(i)
416 this%send_img(i) = this%send_pe(i) + 1
417 end do
418 ! Sized for up to GS_VEC_NC components; scalar path uses the first
419 ! send_total elements, the fused vector path uses nc*send_total.
420 allocate(this%send_buf(max(1, gs_vec_nc*send_total)))
421
422 ! Symmetric coarray sized to twice the global max total receive
423 ! count (double buffering). gs_caf_buf_size tracks the size of one
424 ! half. Grow the shared buffer on demand; allocate / deallocate of
425 ! an allocatable coarray is implicitly collective and acts as a
426 ! global sync.
427 max_total = recv_total
428 call co_max(max_total)
429 max_total = max(1, max_total)
430 ! Half size = GS_VEC_NC * max_total, so the single double-buffered coarray
431 ! serves both the scalar path (which uses only the low max_total of each
432 ! half) and the fused vector path (which uses the full half). gs_caf_buf_size
433 ! is the half size; half_off = parity * gs_caf_buf_size in both paths.
434 if (gs_vec_nc * max_total .gt. gs_caf_buf_size) then
435 if (allocated(gs_caf_recv_buf)) deallocate(gs_caf_recv_buf)
436 allocate(gs_caf_recv_buf(2 * gs_vec_nc * max_total)[*])
437 gs_caf_buf_size = gs_vec_nc * max_total
438 end if
439 this%vec_supported = .true.
440 ! The vector slabs are part of the symmetric/registered allocation
441 ! made above, which every rank has to take part in, so they cannot
442 ! be deferred to the first fused exchange: a rank with no shared
443 ! dofs never reaches it. See gs_comm_t%vec_ready.
444 this%vec_ready = .true.
445
446 ! Tell each sender at what offset in our recv_buf to place their slab,
447 ! and learn at what offset in each receiver's recv_buf our slab should go.
448 ! Each image puts its own offset for each sender into a slot on the
449 ! sender's image indexed by our rank; after sync_all, each image reads
450 ! the offsets directly from its local copy.
451 me = this_image()
452 allocate(dest_xchg(0:pe_size - 1)[*])
453 do i = 1, nrecv
454 dest_xchg(me - 1)[this%recv_img(i)] = this%recv_offset(i)
455 end do
456 sync all
457 do i = 1, nsend
458 this%dest_offset(i) = dest_xchg(this%send_pe(i))
459 end do
460 deallocate(dest_xchg)
461
462 ! Peer set = union of send and recv peers. Both endpoints of every
463 ! neighbour pair include each other, so the two sides advance the pair's
464 ! double-buffer parity on the same rounds (and, in sync mode, their
465 ! pairwise sync images statements match up).
466 allocate(in_neigh(0:pe_size - 1))
467 in_neigh = .false.
468 do i = 1, nsend
469 in_neigh(this%send_pe(i)) = .true.
470 end do
471 do i = 1, nrecv
472 in_neigh(this%recv_pe(i)) = .true.
473 end do
474 n_neigh = count(in_neigh)
475 allocate(this%peer(n_neigh))
476 n_neigh = 0
477 do i = 0, pe_size - 1
478 if (in_neigh(i)) then
479 n_neigh = n_neigh + 1
480 this%peer(n_neigh) = i
481 end if
482 end do
483 deallocate(in_neigh)
484
485 ! Shared by every instance, like the buffer it indexes, so allocate it
486 ! once and never reset it: the parity of a pair has to keep advancing
487 ! across instances.
488 if (.not. allocated(gs_caf_peer_parity)) then
489 allocate(gs_caf_peer_parity(0:pe_size - 1))
490 gs_caf_peer_parity = 0
491 end if
492
493 if (gs_caf_mode .eq. gs_caf_signal_sync) then
494 allocate(this%sync_img(size(this%peer)))
495 this%sync_img = this%peer + 1
496#ifdef HAVE_COARRAY_EVENTS
497 else if (gs_caf_mode .eq. gs_caf_signal_event) then
498 ! Start from zero event counts; see gs_caf_event_drain for why an
499 ! instance leaves credits behind and what accumulating them costs.
500 call gs_caf_event_drain()
501 ! No gs op can be in flight here (init is collective), so any in-use
502 ! flag left set by a torn-down instance is stale.
503 gs_caf_event_in_use = .false.
504#endif
505 end if ! atomic mode: no per-instance state to allocate
506
507 ! Ensure recv_buf is allocated and (atomic mode) baselines are stable
508 ! on every image before any signalling activity begins.
509 sync all
510#else
511 call neko_error("Coarray Fortran support not built; reconfigure with " // &
512 "a coarray-capable Fortran compiler")
513#endif
514 end subroutine gs_caf_init
515
519 subroutine gs_caf_free(this)
520 class(gs_caf_t), intent(inout) :: this
521#ifdef HAVE_COARRAY
522 if (allocated(this%send_buf)) deallocate(this%send_buf)
523 if (allocated(this%send_len)) deallocate(this%send_len)
524 if (allocated(this%recv_len)) deallocate(this%recv_len)
525 if (allocated(this%send_offset)) deallocate(this%send_offset)
526 if (allocated(this%recv_offset)) deallocate(this%recv_offset)
527 if (allocated(this%dest_offset)) deallocate(this%dest_offset)
528 if (allocated(this%send_img)) deallocate(this%send_img)
529 if (allocated(this%recv_img)) deallocate(this%recv_img)
530 if (allocated(this%peer)) deallocate(this%peer)
531 if (allocated(this%sync_img)) deallocate(this%sync_img)
532
533 call this%free_order()
534 call this%free_dofs()
535#endif
536 end subroutine gs_caf_free
537
538#ifdef HAVE_COARRAY
539
540#ifdef HAVE_COARRAY_EVENTS
541
561 subroutine gs_caf_event_drain()
562 integer :: pending
563
564 call event_query(gs_caf_buf_ready_ev, pending)
565 if (pending .gt. 0) then
566 event wait(gs_caf_buf_ready_ev, until_count=pending)
567 end if
568
569 call event_query(gs_caf_data_ready_ev, pending)
570 if (pending .gt. 0) then
571 event wait(gs_caf_data_ready_ev, until_count=pending)
572 end if
573
574 end subroutine gs_caf_event_drain
575
576#endif
577#endif
578
584 subroutine gs_nbsend_caf(this, u, n, tag, deps, strm)
585 class(gs_caf_t), intent(inout) :: this
586 integer, intent(in) :: n
587 real(kind=rp), dimension(n), intent(inout) :: u
588 integer, intent(in) :: tag
589 type(c_ptr), intent(inout) :: deps
590 type(c_ptr), intent(inout) :: strm
591#ifdef HAVE_COARRAY
592 integer :: i, j, dst, off, dimg, ndst, doff, half_off
593 integer, pointer :: sp(:)
594 integer(kind=atomic_int_kind) :: flag
595 integer :: me_rank, thrdid
596
597 thrdid = 0
598 !$ thrdid = omp_get_thread_num()
599
600 ! These routines are entered from inside the gs_op_vector OpenMP
601 ! parallel region (the CAF backend is a host comm method), so the whole
602 ! thread team executes them. Coarray Fortran image control is not
603 ! thread-safe: every put, sync, atomic_* and event statement -- and the
604 ! shared module/instance signalling state -- is performed by the master
605 ! thread alone, inside !$omp master regions. Only the local pack of the
606 ! send buffer is work-shared across the team with !$omp do. The implicit
607 ! barrier at the end of each !$omp do guarantees the slab is fully packed
608 ! before the master thread reads it for the put.
609 !
610 ! parity is flipped by the master at the end of nbwait and published by
611 ! the barrier there, so it is stable for every thread here.
612
613 if (gs_caf_mode .eq. gs_caf_signal_sync) then
614 do i = 1, size(this%send_pe)
615 dst = this%send_pe(i)
616 off = this%send_offset(i)
617 ndst = this%send_len(i)
618 dimg = this%send_img(i)
619 doff = this%dest_offset(i)
620 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
621 sp => this%send_dof(dst)%array()
622 !OCL NORECURRENCE, NOVREC, NOALIAS
623 !DIR$ CONCURRENT
624 !DIR$ IVDEP
625 !GCC$ ivdep
626 !NEC$ IVDEP
627 !$omp do
628 do j = 1, ndst
629 this%send_buf(off + j) = u(sp(j))
630 end do
631 !$omp end do
632 if (thrdid .eq. 0) then
633 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
634 = this%send_buf(off + 1 : off + ndst)
635 end if
636 end do
637 !$omp barrier
638#ifdef HAVE_COARRAY_EVENTS
639 else if (gs_caf_mode .eq. gs_caf_signal_event) then
640 ! Event-mode signalling is a sequence of image-control statements and
641 ! is executed by the master thread alone.
642 if (thrdid .eq. 0) then
643 ! Event mode shares one set of module-level event coarrays among
644 ! all instances and cannot disambiguate posts from concurrent gs
645 ! ops, so we must guarantee non-overlapping nbsend/nbwait windows.
646 if (gs_caf_event_in_use) then
647 call neko_error("Event-mode coarray gather-scatter does not " // &
648 "support overlapping gs ops on different instances")
649 end if
650 gs_caf_event_in_use = .true.
651
652 ! Wait for all receivers to have credited their buffers (skipped
653 ! on the first nbsend; there are no credits posted yet).
654 if (this%send_started) then
655 if (size(this%send_pe) .gt. 0) then
656 event wait(gs_caf_buf_ready_ev, until_count=size(this%send_pe))
657 end if
658 else
659 this%send_started = .true.
660 end if
661 end if
662 ! The back-pressure wait above must complete before any put below
663 ! overwrites the receivers' buffers.
664 !$omp barrier
665
666 do i = 1, size(this%send_pe)
667 dst = this%send_pe(i)
668 off = this%send_offset(i)
669 ndst = this%send_len(i)
670 dimg = this%send_img(i)
671 doff = this%dest_offset(i)
672 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
673 sp => this%send_dof(dst)%array()
674 !OCL NORECURRENCE, NOVREC, NOALIAS
675 !DIR$ CONCURRENT
676 !DIR$ IVDEP
677 !GCC$ ivdep
678 !NEC$ IVDEP
679 !$omp do
680 do j = 1, ndst
681 this%send_buf(off + j) = u(sp(j))
682 end do
683 !$omp end do
684 if (thrdid .eq. 0) then
685 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
686 = this%send_buf(off + 1 : off + ndst)
687 ! event post is meant to act as an image-control statement
688 ! that establishes segment ordering with the matching event
689 ! wait, but real-world coarray runtimes can let a small event
690 ! message race past a still-in-flight RDMA put -- the
691 ! receiver's wait then completes before the data has landed.
692 ! sync memory forces the put to commit locally before the post.
693 sync memory
694 event post(gs_caf_data_ready_ev[dimg])
695 end if
696 end do
697 !$omp barrier
698#endif
699 else
700 ! Pack all peers up front (work-shared) so the subsequent network
701 ! waits and puts can overlap with each other rather than serialising
702 ! behind per-peer pack work.
703 do i = 1, size(this%send_pe)
704 dst = this%send_pe(i)
705 off = this%send_offset(i)
706 ndst = this%send_len(i)
707 sp => this%send_dof(dst)%array()
708 !OCL NORECURRENCE, NOVREC, NOALIAS
709 !DIR$ CONCURRENT
710 !DIR$ IVDEP
711 !GCC$ ivdep
712 !NEC$ IVDEP
713 !$omp do
714 do j = 1, ndst
715 this%send_buf(off + j) = u(sp(j))
716 end do
717 !$omp end do
718 end do
719
720 ! Back-pressure, put and signal per peer -- all coarray operations,
721 ! so master only. With double-buffering the half we are about to
722 ! write last carried round (send_count - 2), so we only need the
723 ! receiver to have unpacked through (send_count - 1).
724 if (thrdid .eq. 0) then
725 me_rank = this_image() - 1
726 do i = 1, size(this%send_pe)
727 dst = this%send_pe(i)
728 off = this%send_offset(i)
729 ndst = this%send_len(i)
730 dimg = this%send_img(i)
731 doff = this%dest_offset(i)
732 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
733
734 do
735 call atomic_ref(flag, gs_caf_buf_ready(this%send_pe(i)))
736 if (int(flag) .ge. gs_caf_send_count(this%send_pe(i)) - 1) exit
737 end do
738
739 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
740 = this%send_buf(off + 1 : off + ndst)
741
742 gs_caf_send_count(this%send_pe(i)) = &
743 gs_caf_send_count(this%send_pe(i)) + 1
744 call atomic_define(gs_caf_data_ready(me_rank)[dimg], &
745 int(gs_caf_send_count(this%send_pe(i)), atomic_int_kind))
746 end do
747 end if
748 !$omp barrier
749 end if
750#else
751 call neko_error("Coarray Fortran support not built")
752#endif
753 end subroutine gs_nbsend_caf
754
757 subroutine gs_nbrecv_caf(this, tag)
758 class(gs_caf_t), intent(inout) :: this
759 integer, intent(in) :: tag
760 end subroutine gs_nbrecv_caf
761
766 subroutine gs_nbwait_caf(this, u, n, op, strm)
767 class(gs_caf_t), intent(inout) :: this
768 integer, intent(in) :: n
769 real(kind=rp), dimension(n), intent(inout) :: u
770 type(c_ptr), intent(inout) :: strm
771 integer :: op
772#ifdef HAVE_COARRAY
773 integer :: i, j, src, off, nsrc, half_off
774 integer, pointer :: sp(:)
775 integer(kind=atomic_int_kind) :: flag
776 integer :: me_rank, thrdid
777
778 thrdid = 0
779 !$ thrdid = omp_get_thread_num()
780
781 ! All incoming data is awaited by the master (see gs_nbsend_caf on why
782 ! coarray operations are funnelled); the barrier then releases the team
783 ! into the unpack once every slab has landed.
784 if (thrdid .eq. 0) then
785 if (gs_caf_mode .eq. gs_caf_signal_sync) then
786 if (allocated(this%sync_img)) then
787 if (size(this%sync_img) .gt. 0) then
788 sync images(this%sync_img)
789 end if
790 end if
791#ifdef HAVE_COARRAY_EVENTS
792 else if (gs_caf_mode .eq. gs_caf_signal_event) then
793 if (size(this%recv_pe) .gt. 0) then
794 event wait(gs_caf_data_ready_ev, until_count=size(this%recv_pe))
795 end if
796#endif
797 else
798 ! Atomic mode: spin per-sender on data_ready until the expected
799 ! round count is observed.
800 do i = 1, size(this%recv_pe)
801 gs_caf_recv_count(this%recv_pe(i)) = &
802 gs_caf_recv_count(this%recv_pe(i)) + 1
803 do
804 call atomic_ref(flag, gs_caf_data_ready(this%recv_pe(i)))
805 if (int(flag) .ge. gs_caf_recv_count(this%recv_pe(i))) exit
806 end do
807 end do
808 end if
809 end if
810 !$omp barrier
811
812 ! Reduce each received slab into u. The loop over peers stays serial: a
813 ! dof shared by 3+ ranks appears in several recv_dof lists, so reducing
814 ! two slabs concurrently would race on that dof. Parallelism is taken
815 ! within each slab instead.
816 do i = 1, size(this%recv_pe)
817 src = this%recv_pe(i)
818 off = this%recv_offset(i)
819 nsrc = this%recv_len(i)
820 half_off = gs_caf_peer_parity(src) * gs_caf_buf_size
821 sp => this%recv_dof(src)%array()
822 select case (op)
823 case (gs_op_add)
824 !OCL NORECURRENCE, NOVREC, NOALIAS
825 !DIR$ CONCURRENT
826 !DIR$ IVDEP
827 !GCC$ ivdep
828 !NEC$ IVDEP
829 !$omp do
830 do j = 1, nsrc
831 u(sp(j)) = u(sp(j)) + gs_caf_recv_buf(half_off + off + j)
832 end do
833 !$omp end do
834 case (gs_op_mul)
835 !OCL NORECURRENCE, NOVREC, NOALIAS
836 !DIR$ CONCURRENT
837 !DIR$ IVDEP
838 !GCC$ ivdep
839 !NEC$ IVDEP
840 !$omp do
841 do j = 1, nsrc
842 u(sp(j)) = u(sp(j)) * gs_caf_recv_buf(half_off + off + j)
843 end do
844 !$omp end do
845 case (gs_op_min)
846 !OCL NORECURRENCE, NOVREC, NOALIAS
847 !DIR$ CONCURRENT
848 !DIR$ IVDEP
849 !GCC$ ivdep
850 !NEC$ IVDEP
851 !$omp do
852 do j = 1, nsrc
853 u(sp(j)) = min(u(sp(j)), gs_caf_recv_buf(half_off + off + j))
854 end do
855 !$omp end do
856 case (gs_op_max)
857 !OCL NORECURRENCE, NOVREC, NOALIAS
858 !DIR$ CONCURRENT
859 !DIR$ IVDEP
860 !GCC$ ivdep
861 !NEC$ IVDEP
862 !$omp do
863 do j = 1, nsrc
864 u(sp(j)) = max(u(sp(j)), gs_caf_recv_buf(half_off + off + j))
865 end do
866 !$omp end do
867 case default
868 call neko_error("Unknown operation in gs_nbwait_caf")
869 end select
870 end do
871
872 ! The implicit barrier at the last end do guarantees every slab has been
873 ! consumed before the master credits the senders and flips the parity;
874 ! the trailing barrier publishes the new parity to the whole team.
875 if (thrdid .eq. 0) then
876 if (gs_caf_mode .eq. gs_caf_signal_atomic) then
877 ! Credit each sender that we have unpacked their slab so they
878 ! may proceed with their next round.
879 me_rank = this_image() - 1
880 do i = 1, size(this%recv_pe)
881 call atomic_define(gs_caf_buf_ready(me_rank)[this%recv_img(i)], &
882 int(gs_caf_recv_count(this%recv_pe(i)), atomic_int_kind))
883 end do
884#ifdef HAVE_COARRAY_EVENTS
885 else if (gs_caf_mode .eq. gs_caf_signal_event) then
886 do i = 1, size(this%recv_pe)
887 event post(gs_caf_buf_ready_ev[this%recv_img(i)])
888 end do
889 gs_caf_event_in_use = .false.
890#endif
891 end if
892
893 ! Flip the double-buffer parity of every peer this round involved, so
894 ! our next exchange with each of them uses the other half.
895 do i = 1, size(this%peer)
896 gs_caf_peer_parity(this%peer(i)) = &
897 1 - gs_caf_peer_parity(this%peer(i))
898 end do
899 end if
900 !$omp barrier
901#else
902 call neko_error("Coarray Fortran support not built")
903#endif
904 end subroutine gs_nbwait_caf
905
910 subroutine gs_nbsend_vec_caf(this, u, n, nc, tag, deps, strm)
911 class(gs_caf_t), intent(inout) :: this
912 integer, intent(in) :: n, nc
913 real(kind=rp), dimension(nc*n), intent(inout) :: u
914 integer, intent(in) :: tag
915 type(c_ptr), intent(inout) :: deps
916 type(c_ptr), intent(inout) :: strm
917#ifdef HAVE_COARRAY
918 integer :: i, j, c, dst, off, dimg, ndst, doff, half_off
919 integer, pointer :: sp(:)
920 integer(kind=atomic_int_kind) :: flag
921 integer :: me_rank, thrdid
922
923 thrdid = 0
924 !$ thrdid = omp_get_thread_num()
925
926 ! Same threading split as the scalar path: the nc-component pack is
927 ! work-shared over the dofs of one peer at a time, every coarray
928 ! operation is issued by the master alone.
929
930 if (gs_caf_mode .eq. gs_caf_signal_sync) then
931 do i = 1, size(this%send_pe)
932 dst = this%send_pe(i)
933 off = this%send_offset(i)
934 ndst = this%send_len(i)
935 dimg = this%send_img(i)
936 doff = this%dest_offset(i)
937 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
938 sp => this%send_dof(dst)%array()
939 !$omp do
940 do j = 1, ndst
941 do c = 1, nc
942 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
943 end do
944 end do
945 !$omp end do
946 if (thrdid .eq. 0) then
947 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
948 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
949 end if
950 end do
951 !$omp barrier
952#ifdef HAVE_COARRAY_EVENTS
953 else if (gs_caf_mode .eq. gs_caf_signal_event) then
954 if (thrdid .eq. 0) then
955 if (gs_caf_event_in_use) then
956 call neko_error("Event-mode coarray gather-scatter does not " // &
957 "support overlapping gs ops on different instances")
958 end if
959 gs_caf_event_in_use = .true.
960
961 if (this%send_started) then
962 if (size(this%send_pe) .gt. 0) then
963 event wait(gs_caf_buf_ready_ev, until_count=size(this%send_pe))
964 end if
965 else
966 this%send_started = .true.
967 end if
968 end if
969 !$omp barrier
970
971 do i = 1, size(this%send_pe)
972 dst = this%send_pe(i)
973 off = this%send_offset(i)
974 ndst = this%send_len(i)
975 dimg = this%send_img(i)
976 doff = this%dest_offset(i)
977 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
978 sp => this%send_dof(dst)%array()
979 !$omp do
980 do j = 1, ndst
981 do c = 1, nc
982 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
983 end do
984 end do
985 !$omp end do
986 if (thrdid .eq. 0) then
987 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
988 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
989 ! event post is meant to act as an image-control statement
990 ! that establishes segment ordering with the matching event
991 ! wait, but real-world coarray runtimes can let a small event
992 ! message race past a still-in-flight RDMA put -- the
993 ! receiver's wait then completes before the data has landed.
994 ! sync memory forces the put to commit locally before the post.
995 sync memory
996 event post(gs_caf_data_ready_ev[dimg])
997 end if
998 end do
999 !$omp barrier
1000#endif
1001 else
1002 do i = 1, size(this%send_pe)
1003 dst = this%send_pe(i)
1004 off = this%send_offset(i)
1005 ndst = this%send_len(i)
1006 sp => this%send_dof(dst)%array()
1007 !$omp do
1008 do j = 1, ndst
1009 do c = 1, nc
1010 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
1011 end do
1012 end do
1013 !$omp end do
1014 end do
1015
1016 if (thrdid .eq. 0) then
1017 me_rank = this_image() - 1
1018 do i = 1, size(this%send_pe)
1019 dst = this%send_pe(i)
1020 off = this%send_offset(i)
1021 ndst = this%send_len(i)
1022 dimg = this%send_img(i)
1023 doff = this%dest_offset(i)
1024 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
1025
1026 do
1027 call atomic_ref(flag, gs_caf_buf_ready(this%send_pe(i)))
1028 if (int(flag) .ge. gs_caf_send_count(this%send_pe(i)) - 1) exit
1029 end do
1030
1031 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
1032 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
1033
1034 gs_caf_send_count(this%send_pe(i)) = &
1035 gs_caf_send_count(this%send_pe(i)) + 1
1036 call atomic_define(gs_caf_data_ready(me_rank)[dimg], &
1037 int(gs_caf_send_count(this%send_pe(i)), atomic_int_kind))
1038 end do
1039 end if
1040 !$omp barrier
1041 end if
1042#else
1043 call neko_error("Coarray Fortran support not built")
1044#endif
1045 end subroutine gs_nbsend_vec_caf
1046
1048 subroutine gs_nbrecv_vec_caf(this, tag, nc)
1049 class(gs_caf_t), intent(inout) :: this
1050 integer, intent(in) :: tag, nc
1051 end subroutine gs_nbrecv_vec_caf
1052
1054 subroutine gs_nbwait_vec_caf(this, u, n, nc, op, strm)
1055 class(gs_caf_t), intent(inout) :: this
1056 integer, intent(in) :: n, nc
1057 real(kind=rp), dimension(nc*n), intent(inout) :: u
1058 type(c_ptr), intent(inout) :: strm
1059 integer :: op
1060#ifdef HAVE_COARRAY
1061 integer :: i, j, c, src, off, nsrc, half_off
1062 integer, pointer :: sp(:)
1063 integer(kind=atomic_int_kind) :: flag
1064 integer :: me_rank, thrdid
1065
1066 thrdid = 0
1067 !$ thrdid = omp_get_thread_num()
1068 if (thrdid .eq. 0) then
1069 if (gs_caf_mode .eq. gs_caf_signal_sync) then
1070 if (allocated(this%sync_img)) then
1071 if (size(this%sync_img) .gt. 0) then
1072 sync images(this%sync_img)
1073 end if
1074 end if
1075#ifdef HAVE_COARRAY_EVENTS
1076 else if (gs_caf_mode .eq. gs_caf_signal_event) then
1077 if (size(this%recv_pe) .gt. 0) then
1078 event wait(gs_caf_data_ready_ev, until_count=size(this%recv_pe))
1079 end if
1080#endif
1081 else
1082 do i = 1, size(this%recv_pe)
1083 gs_caf_recv_count(this%recv_pe(i)) = &
1084 gs_caf_recv_count(this%recv_pe(i)) + 1
1085 do
1086 call atomic_ref(flag, gs_caf_data_ready(this%recv_pe(i)))
1087 if (int(flag) .ge. gs_caf_recv_count(this%recv_pe(i))) exit
1088 end do
1089 end do
1090 end if
1091 end if
1092 !$omp barrier
1093
1094 ! Serial over peers (a dof shared by 3+ ranks appears in several recv
1095 ! lists); parallelism is taken within each slab.
1096 do i = 1, size(this%recv_pe)
1097 src = this%recv_pe(i)
1098 off = this%recv_offset(i)
1099 nsrc = this%recv_len(i)
1100 half_off = gs_caf_peer_parity(src) * gs_caf_buf_size
1101 sp => this%recv_dof(src)%array()
1102 select case (op)
1103 case (gs_op_add)
1104 !$omp do
1105 do j = 1, nsrc
1106 do c = 1, nc
1107 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
1108 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j)
1109 end do
1110 end do
1111 !$omp end do
1112 case (gs_op_mul)
1113 !$omp do
1114 do j = 1, nsrc
1115 do c = 1, nc
1116 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
1117 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j)
1118 end do
1119 end do
1120 !$omp end do
1121 case (gs_op_min)
1122 !$omp do
1123 do j = 1, nsrc
1124 do c = 1, nc
1125 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
1126 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j))
1127 end do
1128 end do
1129 !$omp end do
1130 case (gs_op_max)
1131 !$omp do
1132 do j = 1, nsrc
1133 do c = 1, nc
1134 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
1135 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j))
1136 end do
1137 end do
1138 !$omp end do
1139 case default
1140 call neko_error("Unknown operation in gs_nbwait_vec_caf")
1141 end select
1142 end do
1143
1144 if (thrdid .eq. 0) then
1145 if (gs_caf_mode .eq. gs_caf_signal_atomic) then
1146 me_rank = this_image() - 1
1147 do i = 1, size(this%recv_pe)
1148 call atomic_define(gs_caf_buf_ready(me_rank)[this%recv_img(i)], &
1149 int(gs_caf_recv_count(this%recv_pe(i)), atomic_int_kind))
1150 end do
1151#ifdef HAVE_COARRAY_EVENTS
1152 else if (gs_caf_mode .eq. gs_caf_signal_event) then
1153 do i = 1, size(this%recv_pe)
1154 event post(gs_caf_buf_ready_ev[this%recv_img(i)])
1155 end do
1156 gs_caf_event_in_use = .false.
1157#endif
1158 end if
1159
1160 do i = 1, size(this%peer)
1161 gs_caf_peer_parity(this%peer(i)) = 1 - gs_caf_peer_parity(this%peer(i))
1162 end do
1163 end if
1164 !$omp barrier
1165#else
1166 call neko_error("Coarray Fortran support not built")
1167#endif
1168 end subroutine gs_nbwait_vec_caf
1169
1170end module gs_caf
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:62
Defines Coarray Fortran gather-scatter communication.
Definition gs_caf.F90:34
subroutine gs_nbrecv_caf(this, tag)
No-op for coarrays: senders push into the receiver's buffer, so the receive side does not need to pos...
Definition gs_caf.F90:758
subroutine gs_nbrecv_vec_caf(this, tag, nc)
No-op: senders push into the receiver's buffer.
Definition gs_caf.F90:1049
subroutine gs_nbsend_caf(this, u, n, tag, deps, strm)
Pack u into per-peer slabs and put each slab into the remote image's recv_buf. Double buffering means...
Definition gs_caf.F90:585
subroutine gs_caf_init(this, send_pe, recv_pe)
Initialise Coarray Fortran based communication method.
Definition gs_caf.F90:357
subroutine gs_nbwait_caf(this, u, n, op, strm)
Wait for all incoming puts and reduce them into u. In sync mode a sync_images bracket pairs with the ...
Definition gs_caf.F90:767
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, parameter, public gs_caf_signal_event
Definition gs_caf.F90:65
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
subroutine gs_nbsend_vec_caf(this, u, n, nc, tag, deps, strm)
Fused nc-component put. Each peer slab is nc consecutive component blocks; the remote placement offse...
Definition gs_caf.F90:911
integer, parameter, public gs_caf_signal_atomic
Definition gs_caf.F90:64
subroutine gs_caf_free(this)
Deallocate Coarray Fortran based communication method. The shared module-level recv coarray is intent...
Definition gs_caf.F90:520
integer, parameter, public gs_caf_signal_sync
Definition gs_caf.F90:63
subroutine gs_nbwait_vec_caf(this, u, n, nc, op, strm)
Fused nc-component wait/reduce for the coarray backend.
Definition gs_caf.F90:1055
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 a gather-scatter communication method.
Definition gs_comm.f90:34
integer, parameter, public gs_vec_nc
Maximum number of components handled by the fused vector (multi-component) halo exchange used by gs_o...
Definition gs_comm.f90:50
Defines Gather-scatter operations.
Definition gs_ops.f90:34
integer, parameter, public gs_op_add
Definition gs_ops.f90:36
integer, parameter, public gs_op_max
Definition gs_ops.f90:36
integer, parameter, public gs_op_min
Definition gs_ops.f90:36
integer, parameter, public gs_op_mul
Definition gs_ops.f90:36
integer, parameter, public sp
Definition num_types.f90:8
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication using Coarray Fortran (F2008). Each image puts directly into the (module...
Definition gs_caf.F90:171
Gather-scatter communication method.
Definition gs_comm.f90:53
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40