Neko 1.99.7
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
441 ! Tell each sender at what offset in our recv_buf to place their slab,
442 ! and learn at what offset in each receiver's recv_buf our slab should go.
443 ! Each image puts its own offset for each sender into a slot on the
444 ! sender's image indexed by our rank; after sync_all, each image reads
445 ! the offsets directly from its local copy.
446 me = this_image()
447 allocate(dest_xchg(0:pe_size - 1)[*])
448 do i = 1, nrecv
449 dest_xchg(me - 1)[this%recv_img(i)] = this%recv_offset(i)
450 end do
451 sync all
452 do i = 1, nsend
453 this%dest_offset(i) = dest_xchg(this%send_pe(i))
454 end do
455 deallocate(dest_xchg)
456
457 ! Peer set = union of send and recv peers. Both endpoints of every
458 ! neighbour pair include each other, so the two sides advance the pair's
459 ! double-buffer parity on the same rounds (and, in sync mode, their
460 ! pairwise sync images statements match up).
461 allocate(in_neigh(0:pe_size - 1))
462 in_neigh = .false.
463 do i = 1, nsend
464 in_neigh(this%send_pe(i)) = .true.
465 end do
466 do i = 1, nrecv
467 in_neigh(this%recv_pe(i)) = .true.
468 end do
469 n_neigh = count(in_neigh)
470 allocate(this%peer(n_neigh))
471 n_neigh = 0
472 do i = 0, pe_size - 1
473 if (in_neigh(i)) then
474 n_neigh = n_neigh + 1
475 this%peer(n_neigh) = i
476 end if
477 end do
478 deallocate(in_neigh)
479
480 ! Shared by every instance, like the buffer it indexes, so allocate it
481 ! once and never reset it: the parity of a pair has to keep advancing
482 ! across instances.
483 if (.not. allocated(gs_caf_peer_parity)) then
484 allocate(gs_caf_peer_parity(0:pe_size - 1))
485 gs_caf_peer_parity = 0
486 end if
487
488 if (gs_caf_mode .eq. gs_caf_signal_sync) then
489 allocate(this%sync_img(size(this%peer)))
490 this%sync_img = this%peer + 1
491#ifdef HAVE_COARRAY_EVENTS
492 else if (gs_caf_mode .eq. gs_caf_signal_event) then
493 ! Start from zero event counts; see gs_caf_event_drain for why an
494 ! instance leaves credits behind and what accumulating them costs.
495 call gs_caf_event_drain()
496 ! No gs op can be in flight here (init is collective), so any in-use
497 ! flag left set by a torn-down instance is stale.
498 gs_caf_event_in_use = .false.
499#endif
500 end if ! atomic mode: no per-instance state to allocate
501
502 ! Ensure recv_buf is allocated and (atomic mode) baselines are stable
503 ! on every image before any signalling activity begins.
504 sync all
505#else
506 call neko_error("Coarray Fortran support not built; reconfigure with " // &
507 "a coarray-capable Fortran compiler")
508#endif
509 end subroutine gs_caf_init
510
514 subroutine gs_caf_free(this)
515 class(gs_caf_t), intent(inout) :: this
516#ifdef HAVE_COARRAY
517 if (allocated(this%send_buf)) deallocate(this%send_buf)
518 if (allocated(this%send_len)) deallocate(this%send_len)
519 if (allocated(this%recv_len)) deallocate(this%recv_len)
520 if (allocated(this%send_offset)) deallocate(this%send_offset)
521 if (allocated(this%recv_offset)) deallocate(this%recv_offset)
522 if (allocated(this%dest_offset)) deallocate(this%dest_offset)
523 if (allocated(this%send_img)) deallocate(this%send_img)
524 if (allocated(this%recv_img)) deallocate(this%recv_img)
525 if (allocated(this%peer)) deallocate(this%peer)
526 if (allocated(this%sync_img)) deallocate(this%sync_img)
527
528 call this%free_order()
529 call this%free_dofs()
530#endif
531 end subroutine gs_caf_free
532
533#ifdef HAVE_COARRAY
534
535#ifdef HAVE_COARRAY_EVENTS
536
556 subroutine gs_caf_event_drain()
557 integer :: pending
558
559 call event_query(gs_caf_buf_ready_ev, pending)
560 if (pending .gt. 0) then
561 event wait(gs_caf_buf_ready_ev, until_count=pending)
562 end if
563
564 call event_query(gs_caf_data_ready_ev, pending)
565 if (pending .gt. 0) then
566 event wait(gs_caf_data_ready_ev, until_count=pending)
567 end if
568
569 end subroutine gs_caf_event_drain
570
571#endif
572#endif
573
579 subroutine gs_nbsend_caf(this, u, n, tag, deps, strm)
580 class(gs_caf_t), intent(inout) :: this
581 integer, intent(in) :: n
582 real(kind=rp), dimension(n), intent(inout) :: u
583 integer, intent(in) :: tag
584 type(c_ptr), intent(inout) :: deps
585 type(c_ptr), intent(inout) :: strm
586#ifdef HAVE_COARRAY
587 integer :: i, j, dst, off, dimg, ndst, doff, half_off
588 integer, pointer :: sp(:)
589 integer(kind=atomic_int_kind) :: flag
590 integer :: me_rank, thrdid
591
592 thrdid = 0
593 !$ thrdid = omp_get_thread_num()
594
595 ! These routines are entered from inside the gs_op_vector OpenMP
596 ! parallel region (the CAF backend is a host comm method), so the whole
597 ! thread team executes them. Coarray Fortran image control is not
598 ! thread-safe: every put, sync, atomic_* and event statement -- and the
599 ! shared module/instance signalling state -- is performed by the master
600 ! thread alone, inside !$omp master regions. Only the local pack of the
601 ! send buffer is work-shared across the team with !$omp do. The implicit
602 ! barrier at the end of each !$omp do guarantees the slab is fully packed
603 ! before the master thread reads it for the put.
604 !
605 ! parity is flipped by the master at the end of nbwait and published by
606 ! the barrier there, so it is stable for every thread here.
607
608 if (gs_caf_mode .eq. gs_caf_signal_sync) then
609 do i = 1, size(this%send_pe)
610 dst = this%send_pe(i)
611 off = this%send_offset(i)
612 ndst = this%send_len(i)
613 dimg = this%send_img(i)
614 doff = this%dest_offset(i)
615 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
616 sp => this%send_dof(dst)%array()
617 !OCL NORECURRENCE, NOVREC, NOALIAS
618 !DIR$ CONCURRENT
619 !DIR$ IVDEP
620 !GCC$ ivdep
621 !NEC$ IVDEP
622 !$omp do
623 do j = 1, ndst
624 this%send_buf(off + j) = u(sp(j))
625 end do
626 !$omp end do
627 if (thrdid .eq. 0) then
628 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
629 = this%send_buf(off + 1 : off + ndst)
630 end if
631 end do
632 !$omp barrier
633#ifdef HAVE_COARRAY_EVENTS
634 else if (gs_caf_mode .eq. gs_caf_signal_event) then
635 ! Event-mode signalling is a sequence of image-control statements and
636 ! is executed by the master thread alone.
637 if (thrdid .eq. 0) then
638 ! Event mode shares one set of module-level event coarrays among
639 ! all instances and cannot disambiguate posts from concurrent gs
640 ! ops, so we must guarantee non-overlapping nbsend/nbwait windows.
641 if (gs_caf_event_in_use) then
642 call neko_error("Event-mode coarray gather-scatter does not " // &
643 "support overlapping gs ops on different instances")
644 end if
645 gs_caf_event_in_use = .true.
646
647 ! Wait for all receivers to have credited their buffers (skipped
648 ! on the first nbsend; there are no credits posted yet).
649 if (this%send_started) then
650 if (size(this%send_pe) .gt. 0) then
651 event wait(gs_caf_buf_ready_ev, until_count=size(this%send_pe))
652 end if
653 else
654 this%send_started = .true.
655 end if
656 end if
657 ! The back-pressure wait above must complete before any put below
658 ! overwrites the receivers' buffers.
659 !$omp barrier
660
661 do i = 1, size(this%send_pe)
662 dst = this%send_pe(i)
663 off = this%send_offset(i)
664 ndst = this%send_len(i)
665 dimg = this%send_img(i)
666 doff = this%dest_offset(i)
667 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
668 sp => this%send_dof(dst)%array()
669 !OCL NORECURRENCE, NOVREC, NOALIAS
670 !DIR$ CONCURRENT
671 !DIR$ IVDEP
672 !GCC$ ivdep
673 !NEC$ IVDEP
674 !$omp do
675 do j = 1, ndst
676 this%send_buf(off + j) = u(sp(j))
677 end do
678 !$omp end do
679 if (thrdid .eq. 0) then
680 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
681 = this%send_buf(off + 1 : off + ndst)
682 ! event post is meant to act as an image-control statement
683 ! that establishes segment ordering with the matching event
684 ! wait, but real-world coarray runtimes can let a small event
685 ! message race past a still-in-flight RDMA put -- the
686 ! receiver's wait then completes before the data has landed.
687 ! sync memory forces the put to commit locally before the post.
688 sync memory
689 event post(gs_caf_data_ready_ev[dimg])
690 end if
691 end do
692 !$omp barrier
693#endif
694 else
695 ! Pack all peers up front (work-shared) so the subsequent network
696 ! waits and puts can overlap with each other rather than serialising
697 ! behind per-peer pack work.
698 do i = 1, size(this%send_pe)
699 dst = this%send_pe(i)
700 off = this%send_offset(i)
701 ndst = this%send_len(i)
702 sp => this%send_dof(dst)%array()
703 !OCL NORECURRENCE, NOVREC, NOALIAS
704 !DIR$ CONCURRENT
705 !DIR$ IVDEP
706 !GCC$ ivdep
707 !NEC$ IVDEP
708 !$omp do
709 do j = 1, ndst
710 this%send_buf(off + j) = u(sp(j))
711 end do
712 !$omp end do
713 end do
714
715 ! Back-pressure, put and signal per peer -- all coarray operations,
716 ! so master only. With double-buffering the half we are about to
717 ! write last carried round (send_count - 2), so we only need the
718 ! receiver to have unpacked through (send_count - 1).
719 if (thrdid .eq. 0) then
720 me_rank = this_image() - 1
721 do i = 1, size(this%send_pe)
722 dst = this%send_pe(i)
723 off = this%send_offset(i)
724 ndst = this%send_len(i)
725 dimg = this%send_img(i)
726 doff = this%dest_offset(i)
727 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
728
729 do
730 call atomic_ref(flag, gs_caf_buf_ready(this%send_pe(i)))
731 if (int(flag) .ge. gs_caf_send_count(this%send_pe(i)) - 1) exit
732 end do
733
734 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
735 = this%send_buf(off + 1 : off + ndst)
736
737 gs_caf_send_count(this%send_pe(i)) = &
738 gs_caf_send_count(this%send_pe(i)) + 1
739 call atomic_define(gs_caf_data_ready(me_rank)[dimg], &
740 int(gs_caf_send_count(this%send_pe(i)), atomic_int_kind))
741 end do
742 end if
743 !$omp barrier
744 end if
745#else
746 call neko_error("Coarray Fortran support not built")
747#endif
748 end subroutine gs_nbsend_caf
749
752 subroutine gs_nbrecv_caf(this, tag)
753 class(gs_caf_t), intent(inout) :: this
754 integer, intent(in) :: tag
755 end subroutine gs_nbrecv_caf
756
761 subroutine gs_nbwait_caf(this, u, n, op, strm)
762 class(gs_caf_t), intent(inout) :: this
763 integer, intent(in) :: n
764 real(kind=rp), dimension(n), intent(inout) :: u
765 type(c_ptr), intent(inout) :: strm
766 integer :: op
767#ifdef HAVE_COARRAY
768 integer :: i, j, src, off, nsrc, half_off
769 integer, pointer :: sp(:)
770 integer(kind=atomic_int_kind) :: flag
771 integer :: me_rank, thrdid
772
773 thrdid = 0
774 !$ thrdid = omp_get_thread_num()
775
776 ! All incoming data is awaited by the master (see gs_nbsend_caf on why
777 ! coarray operations are funnelled); the barrier then releases the team
778 ! into the unpack once every slab has landed.
779 if (thrdid .eq. 0) then
780 if (gs_caf_mode .eq. gs_caf_signal_sync) then
781 if (allocated(this%sync_img)) then
782 if (size(this%sync_img) .gt. 0) then
783 sync images(this%sync_img)
784 end if
785 end if
786#ifdef HAVE_COARRAY_EVENTS
787 else if (gs_caf_mode .eq. gs_caf_signal_event) then
788 if (size(this%recv_pe) .gt. 0) then
789 event wait(gs_caf_data_ready_ev, until_count=size(this%recv_pe))
790 end if
791#endif
792 else
793 ! Atomic mode: spin per-sender on data_ready until the expected
794 ! round count is observed.
795 do i = 1, size(this%recv_pe)
796 gs_caf_recv_count(this%recv_pe(i)) = &
797 gs_caf_recv_count(this%recv_pe(i)) + 1
798 do
799 call atomic_ref(flag, gs_caf_data_ready(this%recv_pe(i)))
800 if (int(flag) .ge. gs_caf_recv_count(this%recv_pe(i))) exit
801 end do
802 end do
803 end if
804 end if
805 !$omp barrier
806
807 ! Reduce each received slab into u. The loop over peers stays serial: a
808 ! dof shared by 3+ ranks appears in several recv_dof lists, so reducing
809 ! two slabs concurrently would race on that dof. Parallelism is taken
810 ! within each slab instead.
811 do i = 1, size(this%recv_pe)
812 src = this%recv_pe(i)
813 off = this%recv_offset(i)
814 nsrc = this%recv_len(i)
815 half_off = gs_caf_peer_parity(src) * gs_caf_buf_size
816 sp => this%recv_dof(src)%array()
817 select case (op)
818 case (gs_op_add)
819 !OCL NORECURRENCE, NOVREC, NOALIAS
820 !DIR$ CONCURRENT
821 !DIR$ IVDEP
822 !GCC$ ivdep
823 !NEC$ IVDEP
824 !$omp do
825 do j = 1, nsrc
826 u(sp(j)) = u(sp(j)) + gs_caf_recv_buf(half_off + off + j)
827 end do
828 !$omp end do
829 case (gs_op_mul)
830 !OCL NORECURRENCE, NOVREC, NOALIAS
831 !DIR$ CONCURRENT
832 !DIR$ IVDEP
833 !GCC$ ivdep
834 !NEC$ IVDEP
835 !$omp do
836 do j = 1, nsrc
837 u(sp(j)) = u(sp(j)) * gs_caf_recv_buf(half_off + off + j)
838 end do
839 !$omp end do
840 case (gs_op_min)
841 !OCL NORECURRENCE, NOVREC, NOALIAS
842 !DIR$ CONCURRENT
843 !DIR$ IVDEP
844 !GCC$ ivdep
845 !NEC$ IVDEP
846 !$omp do
847 do j = 1, nsrc
848 u(sp(j)) = min(u(sp(j)), gs_caf_recv_buf(half_off + off + j))
849 end do
850 !$omp end do
851 case (gs_op_max)
852 !OCL NORECURRENCE, NOVREC, NOALIAS
853 !DIR$ CONCURRENT
854 !DIR$ IVDEP
855 !GCC$ ivdep
856 !NEC$ IVDEP
857 !$omp do
858 do j = 1, nsrc
859 u(sp(j)) = max(u(sp(j)), gs_caf_recv_buf(half_off + off + j))
860 end do
861 !$omp end do
862 case default
863 call neko_error("Unknown operation in gs_nbwait_caf")
864 end select
865 end do
866
867 ! The implicit barrier at the last end do guarantees every slab has been
868 ! consumed before the master credits the senders and flips the parity;
869 ! the trailing barrier publishes the new parity to the whole team.
870 if (thrdid .eq. 0) then
871 if (gs_caf_mode .eq. gs_caf_signal_atomic) then
872 ! Credit each sender that we have unpacked their slab so they
873 ! may proceed with their next round.
874 me_rank = this_image() - 1
875 do i = 1, size(this%recv_pe)
876 call atomic_define(gs_caf_buf_ready(me_rank)[this%recv_img(i)], &
877 int(gs_caf_recv_count(this%recv_pe(i)), atomic_int_kind))
878 end do
879#ifdef HAVE_COARRAY_EVENTS
880 else if (gs_caf_mode .eq. gs_caf_signal_event) then
881 do i = 1, size(this%recv_pe)
882 event post(gs_caf_buf_ready_ev[this%recv_img(i)])
883 end do
884 gs_caf_event_in_use = .false.
885#endif
886 end if
887
888 ! Flip the double-buffer parity of every peer this round involved, so
889 ! our next exchange with each of them uses the other half.
890 do i = 1, size(this%peer)
891 gs_caf_peer_parity(this%peer(i)) = &
892 1 - gs_caf_peer_parity(this%peer(i))
893 end do
894 end if
895 !$omp barrier
896#else
897 call neko_error("Coarray Fortran support not built")
898#endif
899 end subroutine gs_nbwait_caf
900
905 subroutine gs_nbsend_vec_caf(this, u, n, nc, tag, deps, strm)
906 class(gs_caf_t), intent(inout) :: this
907 integer, intent(in) :: n, nc
908 real(kind=rp), dimension(nc*n), intent(inout) :: u
909 integer, intent(in) :: tag
910 type(c_ptr), intent(inout) :: deps
911 type(c_ptr), intent(inout) :: strm
912#ifdef HAVE_COARRAY
913 integer :: i, j, c, dst, off, dimg, ndst, doff, half_off
914 integer, pointer :: sp(:)
915 integer(kind=atomic_int_kind) :: flag
916 integer :: me_rank, thrdid
917
918 thrdid = 0
919 !$ thrdid = omp_get_thread_num()
920
921 ! Same threading split as the scalar path: the nc-component pack is
922 ! work-shared over the dofs of one peer at a time, every coarray
923 ! operation is issued by the master alone.
924
925 if (gs_caf_mode .eq. gs_caf_signal_sync) then
926 do i = 1, size(this%send_pe)
927 dst = this%send_pe(i)
928 off = this%send_offset(i)
929 ndst = this%send_len(i)
930 dimg = this%send_img(i)
931 doff = this%dest_offset(i)
932 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
933 sp => this%send_dof(dst)%array()
934 !$omp do
935 do j = 1, ndst
936 do c = 1, nc
937 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
938 end do
939 end do
940 !$omp end do
941 if (thrdid .eq. 0) then
942 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
943 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
944 end if
945 end do
946 !$omp barrier
947#ifdef HAVE_COARRAY_EVENTS
948 else if (gs_caf_mode .eq. gs_caf_signal_event) then
949 if (thrdid .eq. 0) then
950 if (gs_caf_event_in_use) then
951 call neko_error("Event-mode coarray gather-scatter does not " // &
952 "support overlapping gs ops on different instances")
953 end if
954 gs_caf_event_in_use = .true.
955
956 if (this%send_started) then
957 if (size(this%send_pe) .gt. 0) then
958 event wait(gs_caf_buf_ready_ev, until_count=size(this%send_pe))
959 end if
960 else
961 this%send_started = .true.
962 end if
963 end if
964 !$omp barrier
965
966 do i = 1, size(this%send_pe)
967 dst = this%send_pe(i)
968 off = this%send_offset(i)
969 ndst = this%send_len(i)
970 dimg = this%send_img(i)
971 doff = this%dest_offset(i)
972 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
973 sp => this%send_dof(dst)%array()
974 !$omp do
975 do j = 1, ndst
976 do c = 1, nc
977 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
978 end do
979 end do
980 !$omp end do
981 if (thrdid .eq. 0) then
982 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
983 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
984 ! event post is meant to act as an image-control statement
985 ! that establishes segment ordering with the matching event
986 ! wait, but real-world coarray runtimes can let a small event
987 ! message race past a still-in-flight RDMA put -- the
988 ! receiver's wait then completes before the data has landed.
989 ! sync memory forces the put to commit locally before the post.
990 sync memory
991 event post(gs_caf_data_ready_ev[dimg])
992 end if
993 end do
994 !$omp barrier
995#endif
996 else
997 do i = 1, size(this%send_pe)
998 dst = this%send_pe(i)
999 off = this%send_offset(i)
1000 ndst = this%send_len(i)
1001 sp => this%send_dof(dst)%array()
1002 !$omp do
1003 do j = 1, ndst
1004 do c = 1, nc
1005 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
1006 end do
1007 end do
1008 !$omp end do
1009 end do
1010
1011 if (thrdid .eq. 0) then
1012 me_rank = this_image() - 1
1013 do i = 1, size(this%send_pe)
1014 dst = this%send_pe(i)
1015 off = this%send_offset(i)
1016 ndst = this%send_len(i)
1017 dimg = this%send_img(i)
1018 doff = this%dest_offset(i)
1019 half_off = gs_caf_peer_parity(dst) * gs_caf_buf_size
1020
1021 do
1022 call atomic_ref(flag, gs_caf_buf_ready(this%send_pe(i)))
1023 if (int(flag) .ge. gs_caf_send_count(this%send_pe(i)) - 1) exit
1024 end do
1025
1026 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
1027 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
1028
1029 gs_caf_send_count(this%send_pe(i)) = &
1030 gs_caf_send_count(this%send_pe(i)) + 1
1031 call atomic_define(gs_caf_data_ready(me_rank)[dimg], &
1032 int(gs_caf_send_count(this%send_pe(i)), atomic_int_kind))
1033 end do
1034 end if
1035 !$omp barrier
1036 end if
1037#else
1038 call neko_error("Coarray Fortran support not built")
1039#endif
1040 end subroutine gs_nbsend_vec_caf
1041
1043 subroutine gs_nbrecv_vec_caf(this, tag, nc)
1044 class(gs_caf_t), intent(inout) :: this
1045 integer, intent(in) :: tag, nc
1046 end subroutine gs_nbrecv_vec_caf
1047
1049 subroutine gs_nbwait_vec_caf(this, u, n, nc, op, strm)
1050 class(gs_caf_t), intent(inout) :: this
1051 integer, intent(in) :: n, nc
1052 real(kind=rp), dimension(nc*n), intent(inout) :: u
1053 type(c_ptr), intent(inout) :: strm
1054 integer :: op
1055#ifdef HAVE_COARRAY
1056 integer :: i, j, c, src, off, nsrc, half_off
1057 integer, pointer :: sp(:)
1058 integer(kind=atomic_int_kind) :: flag
1059 integer :: me_rank, thrdid
1060
1061 thrdid = 0
1062 !$ thrdid = omp_get_thread_num()
1063 if (thrdid .eq. 0) then
1064 if (gs_caf_mode .eq. gs_caf_signal_sync) then
1065 if (allocated(this%sync_img)) then
1066 if (size(this%sync_img) .gt. 0) then
1067 sync images(this%sync_img)
1068 end if
1069 end if
1070#ifdef HAVE_COARRAY_EVENTS
1071 else if (gs_caf_mode .eq. gs_caf_signal_event) then
1072 if (size(this%recv_pe) .gt. 0) then
1073 event wait(gs_caf_data_ready_ev, until_count=size(this%recv_pe))
1074 end if
1075#endif
1076 else
1077 do i = 1, size(this%recv_pe)
1078 gs_caf_recv_count(this%recv_pe(i)) = &
1079 gs_caf_recv_count(this%recv_pe(i)) + 1
1080 do
1081 call atomic_ref(flag, gs_caf_data_ready(this%recv_pe(i)))
1082 if (int(flag) .ge. gs_caf_recv_count(this%recv_pe(i))) exit
1083 end do
1084 end do
1085 end if
1086 end if
1087 !$omp barrier
1088
1089 ! Serial over peers (a dof shared by 3+ ranks appears in several recv
1090 ! lists); parallelism is taken within each slab.
1091 do i = 1, size(this%recv_pe)
1092 src = this%recv_pe(i)
1093 off = this%recv_offset(i)
1094 nsrc = this%recv_len(i)
1095 half_off = gs_caf_peer_parity(src) * gs_caf_buf_size
1096 sp => this%recv_dof(src)%array()
1097 select case (op)
1098 case (gs_op_add)
1099 !$omp do
1100 do j = 1, nsrc
1101 do c = 1, nc
1102 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
1103 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j)
1104 end do
1105 end do
1106 !$omp end do
1107 case (gs_op_mul)
1108 !$omp do
1109 do j = 1, nsrc
1110 do c = 1, nc
1111 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
1112 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j)
1113 end do
1114 end do
1115 !$omp end do
1116 case (gs_op_min)
1117 !$omp do
1118 do j = 1, nsrc
1119 do c = 1, nc
1120 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
1121 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j))
1122 end do
1123 end do
1124 !$omp end do
1125 case (gs_op_max)
1126 !$omp do
1127 do j = 1, nsrc
1128 do c = 1, nc
1129 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
1130 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j))
1131 end do
1132 end do
1133 !$omp end do
1134 case default
1135 call neko_error("Unknown operation in gs_nbwait_vec_caf")
1136 end select
1137 end do
1138
1139 if (thrdid .eq. 0) then
1140 if (gs_caf_mode .eq. gs_caf_signal_atomic) then
1141 me_rank = this_image() - 1
1142 do i = 1, size(this%recv_pe)
1143 call atomic_define(gs_caf_buf_ready(me_rank)[this%recv_img(i)], &
1144 int(gs_caf_recv_count(this%recv_pe(i)), atomic_int_kind))
1145 end do
1146#ifdef HAVE_COARRAY_EVENTS
1147 else if (gs_caf_mode .eq. gs_caf_signal_event) then
1148 do i = 1, size(this%recv_pe)
1149 event post(gs_caf_buf_ready_ev[this%recv_img(i)])
1150 end do
1151 gs_caf_event_in_use = .false.
1152#endif
1153 end if
1154
1155 do i = 1, size(this%peer)
1156 gs_caf_peer_parity(this%peer(i)) = 1 - gs_caf_peer_parity(this%peer(i))
1157 end do
1158 end if
1159 !$omp barrier
1160#else
1161 call neko_error("Coarray Fortran support not built")
1162#endif
1163 end subroutine gs_nbwait_vec_caf
1164
1165end 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:753
subroutine gs_nbrecv_vec_caf(this, tag, nc)
No-op: senders push into the receiver's buffer.
Definition gs_caf.F90:1044
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:580
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:762
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:906
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:515
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:1050
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:49
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:12
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:52
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40