Neko 1.99.5
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 implicit none
48 private
49
50 ! Signaling mode constants. Selected at first init via the
51 ! NEKO_GS_CAF_SIGNALING environment variable
52 ! ("sync", "atomic", or "event").
53 integer, parameter, public :: gs_caf_signal_sync = 1
54 integer, parameter, public :: gs_caf_signal_atomic = 2
55 integer, parameter, public :: gs_caf_signal_event = 3
56
57#ifdef HAVE_COARRAY
58 ! Module-level receive coarray, shared by all gs_caf_t instances.
59 ! F2008 forbids a derived type from adding a coarray ultimate
60 ! component when its parent type has none, so the coarray buffer is
61 ! held at module scope rather than as a component of gs_caf_t.
62 !
63 ! The buffer is double-buffered: it is allocated to twice the global
64 ! max receive count so that consecutive rounds write to alternating
65 ! halves. In sync mode this eliminates back-pressure entirely (the
66 ! receiver may still be unpacking the previous round, but from a
67 ! different half, so no overwrite hazard exists). In atomic and event
68 ! modes the same property is used to relax the back-pressure spin to
69 ! a one-round tolerance -- the next overwrite is two rounds away, so
70 ! the receiver only needs to be at most one round behind.
71 ! gs_caf_buf_size is the size of one half.
72 !
73 ! Multiple gs_caf_t instances may coexist (each carrying its own
74 ! offset bookkeeping) provided they are used strictly sequentially
75 ! -- no overlapping nbsend/nbwait rounds across instances. The buffer
76 ! is grown on demand to fit the largest gs ever initialised; it is
77 ! never shrunk and is retained for the program lifetime.
78 real(kind=rp), allocatable :: gs_caf_recv_buf(:)[:]
79 integer :: gs_caf_buf_size = 0
80
81 ! Active signaling mode; bound on the first gs_caf_t init from the
82 ! NEKO_GS_CAF_SIGNALING environment variable. Subsequent instances
83 ! must use the same mode (the env var is read once).
84 integer :: gs_caf_mode = 0
85
86 ! Atomic-mode signaling counters, indexed by remote rank.
87 ! gs_caf_data_ready(s_rank) on image r counts rounds image s has put
88 ! into r so far. gs_caf_buf_ready(r_rank) on image s counts rounds
89 ! image r has finished unpacking from s so far. Allocated only in
90 ! atomic mode and shared by all instances.
91 integer(kind=atomic_int_kind), allocatable :: gs_caf_data_ready(:)[:]
92 integer(kind=atomic_int_kind), allocatable :: gs_caf_buf_ready(:)[:]
93
94 ! Local caches of "rounds we have sent to / received from each remote
95 ! rank" -- size pe_size per image, indexed by remote rank. Updated
96 ! locally on every atomic_define / wait completion in nbsend / nbwait.
97 ! Reading these to baseline a new gs_caf_t avoids any remote
98 ! atomic_ref during init, which Cray CCE has historically deadlocked
99 ! on. The values match the remote atomic counters at quiescent
100 ! points (i.e. between gs ops) for symmetric, lockstep gs traffic.
101 integer, allocatable :: gs_caf_send_count(:)
102 integer, allocatable :: gs_caf_recv_count(:)
103
104#ifdef HAVE_COARRAY_EVENTS
105 ! Event-mode signaling. The data_ready event accumulates one post per
106 ! sender per round; buf_ready is the back-channel from receiver to
107 ! sender. Events are scalar coarrays whose count cannot distinguish
108 ! posts coming from different gs_caf_t instances, so event mode is
109 ! restricted to a single live instance at a time.
110 !
111 ! The events are allocatable rather than static module-scope coarrays
112 ! because Cray CCE has historically had layout issues with mixing
113 ! module-scope static coarrays of derived type with allocatable
114 ! coarrays on the symmetric heap; an explicit allocate side-steps
115 ! that.
116 type(event_type), allocatable :: gs_caf_data_ready_ev[:]
117 type(event_type), allocatable :: gs_caf_buf_ready_ev[:]
118 logical :: gs_caf_event_in_use = .false.
119#endif
120#endif
121
129 type, public, extends(gs_comm_t) :: gs_caf_t
131 real(kind=rp), allocatable :: send_buf(:)
133 integer, allocatable :: send_len(:), recv_len(:)
135 integer, allocatable :: send_offset(:), recv_offset(:)
137 integer, allocatable :: dest_offset(:)
139 integer, allocatable :: send_img(:), recv_img(:)
143 integer, allocatable :: sync_img(:)
146 logical :: send_started = .false.
150 integer :: parity = 0
151 contains
152 procedure, pass(this) :: init => gs_caf_init
153 procedure, pass(this) :: free => gs_caf_free
154 procedure, pass(this) :: nbsend => gs_nbsend_caf
155 procedure, pass(this) :: nbrecv => gs_nbrecv_caf
156 procedure, pass(this) :: nbwait => gs_nbwait_caf
157 procedure, pass(this) :: nbsend_vec => gs_nbsend_vec_caf
158 procedure, pass(this) :: nbrecv_vec => gs_nbrecv_vec_caf
159 procedure, pass(this) :: nbwait_vec => gs_nbwait_vec_caf
160 end type gs_caf_t
161
162contains
163
165 subroutine gs_caf_init(this, send_pe, recv_pe)
166 class(gs_caf_t), intent(inout) :: this
167 type(stack_i4_t), intent(inout) :: send_pe
168 type(stack_i4_t), intent(inout) :: recv_pe
169#ifdef HAVE_COARRAY
170 integer, allocatable :: dest_xchg(:)[:]
171 logical, allocatable :: in_neigh(:)
172 integer :: i, nsend, nrecv, send_total, recv_total, max_total, n_neigh
173 integer :: me, env_len
174 character(len=64) :: env_val
175
176 ! Bind the signaling mode on the first init.
177 if (gs_caf_mode .eq. 0) then
178 call get_environment_variable("NEKO_GS_CAF_SIGNALING", env_val, env_len)
179 if (env_len .gt. 0 .and. env_val(1:env_len) .eq. "atomic") then
180 gs_caf_mode = gs_caf_signal_atomic
181 allocate(gs_caf_data_ready(0:pe_size - 1)[*])
182 allocate(gs_caf_buf_ready(0:pe_size - 1)[*])
183 allocate(gs_caf_send_count(0:pe_size - 1))
184 allocate(gs_caf_recv_count(0:pe_size - 1))
185 gs_caf_send_count = 0
186 gs_caf_recv_count = 0
187 ! F2008 forbids mixing atomic and non-atomic accesses on the
188 ! same variable, so initialise via atomic_define rather than
189 ! a regular array assignment.
190 do i = 0, pe_size - 1
191 call atomic_define(gs_caf_data_ready(i), 0_atomic_int_kind)
192 call atomic_define(gs_caf_buf_ready(i), 0_atomic_int_kind)
193 end do
194 else if (env_len .gt. 0 .and. env_val(1:env_len) .eq. "event") then
195#ifdef HAVE_COARRAY_EVENTS
196 gs_caf_mode = gs_caf_signal_event
197#else
198 call neko_error("NEKO_GS_CAF_SIGNALING=event requires a Fortran " // &
199 "compiler with coarray events support")
200#endif
201 else
202 gs_caf_mode = gs_caf_signal_sync
203 end if
204 end if
205
206#ifdef HAVE_COARRAY_EVENTS
207 ! Allocate the shared event coarrays once, lazily, on the first
208 ! event-mode init. The in-use guard against overlapping gs ops is
209 ! enforced in nbsend/nbwait, not here -- multiple gs_caf_t instances
210 ! may be initialised back-to-back.
211 if (gs_caf_mode .eq. gs_caf_signal_event) then
212 if (.not. allocated(gs_caf_data_ready_ev)) then
213 allocate(gs_caf_data_ready_ev[*])
214 allocate(gs_caf_buf_ready_ev[*])
215 end if
216 end if
217#endif
218
219 call this%init_order(send_pe, recv_pe)
220
221 nsend = size(this%send_pe)
222 nrecv = size(this%recv_pe)
223
224 allocate(this%send_len(nsend), this%send_offset(nsend), &
225 this%send_img(nsend), this%dest_offset(nsend))
226 allocate(this%recv_len(nrecv), this%recv_offset(nrecv), &
227 this%recv_img(nrecv))
228
229 ! Local receive layout
230 recv_total = 0
231 do i = 1, nrecv
232 this%recv_len(i) = this%recv_dof(this%recv_pe(i))%size()
233 this%recv_offset(i) = recv_total
234 recv_total = recv_total + this%recv_len(i)
235 this%recv_img(i) = this%recv_pe(i) + 1
236 end do
237
238 ! Local send layout (concatenated per-peer slabs in one buffer)
239 send_total = 0
240 do i = 1, nsend
241 this%send_len(i) = this%send_dof(this%send_pe(i))%size()
242 this%send_offset(i) = send_total
243 send_total = send_total + this%send_len(i)
244 this%send_img(i) = this%send_pe(i) + 1
245 end do
246 ! Sized for up to GS_VEC_NC components; scalar path uses the first
247 ! send_total elements, the fused vector path uses nc*send_total.
248 allocate(this%send_buf(max(1, gs_vec_nc*send_total)))
249
250 ! Symmetric coarray sized to twice the global max total receive
251 ! count (double buffering). gs_caf_buf_size tracks the size of one
252 ! half. Grow the shared buffer on demand; allocate / deallocate of
253 ! an allocatable coarray is implicitly collective and acts as a
254 ! global sync.
255 max_total = recv_total
256 call co_max(max_total)
257 max_total = max(1, max_total)
258 ! Half size = GS_VEC_NC * max_total, so the single double-buffered coarray
259 ! serves both the scalar path (which uses only the low max_total of each
260 ! half) and the fused vector path (which uses the full half). gs_caf_buf_size
261 ! is the half size; half_off = parity * gs_caf_buf_size in both paths.
262 if (gs_vec_nc * max_total .gt. gs_caf_buf_size) then
263 if (allocated(gs_caf_recv_buf)) deallocate(gs_caf_recv_buf)
264 allocate(gs_caf_recv_buf(2 * gs_vec_nc * max_total)[*])
265 gs_caf_buf_size = gs_vec_nc * max_total
266 end if
267 this%vec_supported = .true.
268
269 ! Tell each sender at what offset in our recv_buf to place their slab,
270 ! and learn at what offset in each receiver's recv_buf our slab should go.
271 ! Each image puts its own offset for each sender into a slot on the
272 ! sender's image indexed by our rank; after sync_all, each image reads
273 ! the offsets directly from its local copy.
274 me = this_image()
275 allocate(dest_xchg(0:pe_size - 1)[*])
276 do i = 1, nrecv
277 dest_xchg(me - 1)[this%recv_img(i)] = this%recv_offset(i)
278 end do
279 sync all
280 do i = 1, nsend
281 this%dest_offset(i) = dest_xchg(this%send_pe(i))
282 end do
283 deallocate(dest_xchg)
284
285 if (gs_caf_mode .eq. gs_caf_signal_sync) then
286 ! Sync image set = union of send and recv peers. Both endpoints of
287 ! every neighbour pair must include each other so the pairwise
288 ! sync images statements match up.
289 allocate(in_neigh(0:pe_size - 1))
290 in_neigh = .false.
291 do i = 1, nsend
292 in_neigh(this%send_pe(i)) = .true.
293 end do
294 do i = 1, nrecv
295 in_neigh(this%recv_pe(i)) = .true.
296 end do
297 n_neigh = count(in_neigh)
298 allocate(this%sync_img(n_neigh))
299 n_neigh = 0
300 do i = 0, pe_size - 1
301 if (in_neigh(i)) then
302 n_neigh = n_neigh + 1
303 this%sync_img(n_neigh) = i + 1
304 end if
305 end do
306 deallocate(in_neigh)
307 end if ! atomic & event modes: no per-instance state to allocate
308
309 ! Ensure recv_buf is allocated and (atomic mode) baselines are stable
310 ! on every image before any signalling activity begins.
311 sync all
312#else
313 call neko_error("Coarray Fortran support not built; reconfigure with " // &
314 "a coarray-capable Fortran compiler")
315#endif
316 end subroutine gs_caf_init
317
321 subroutine gs_caf_free(this)
322 class(gs_caf_t), intent(inout) :: this
323#ifdef HAVE_COARRAY
324 if (allocated(this%send_buf)) deallocate(this%send_buf)
325 if (allocated(this%send_len)) deallocate(this%send_len)
326 if (allocated(this%recv_len)) deallocate(this%recv_len)
327 if (allocated(this%send_offset)) deallocate(this%send_offset)
328 if (allocated(this%recv_offset)) deallocate(this%recv_offset)
329 if (allocated(this%dest_offset)) deallocate(this%dest_offset)
330 if (allocated(this%send_img)) deallocate(this%send_img)
331 if (allocated(this%recv_img)) deallocate(this%recv_img)
332 if (allocated(this%sync_img)) deallocate(this%sync_img)
333
334 call this%free_order()
335 call this%free_dofs()
336#endif
337 end subroutine gs_caf_free
338
344 subroutine gs_nbsend_caf(this, u, n, tag, deps, strm)
345 class(gs_caf_t), intent(inout) :: this
346 integer, intent(in) :: n
347 real(kind=rp), dimension(n), intent(inout) :: u
348 integer, intent(in) :: tag
349 type(c_ptr), intent(inout) :: deps
350 type(c_ptr), intent(inout) :: strm
351#ifdef HAVE_COARRAY
352 integer :: i, j, dst, off, dimg, ndst, doff, half_off
353 integer, pointer :: sp(:)
354 integer(kind=atomic_int_kind) :: flag
355 integer :: me_rank
356
357 half_off = this%parity * gs_caf_buf_size
358
359 if (gs_caf_mode .eq. gs_caf_signal_sync) then
360 do i = 1, size(this%send_pe)
361 dst = this%send_pe(i)
362 off = this%send_offset(i)
363 ndst = this%send_len(i)
364 dimg = this%send_img(i)
365 doff = this%dest_offset(i)
366 sp => this%send_dof(dst)%array()
367 do concurrent(j = 1:ndst)
368 this%send_buf(off + j) = u(sp(j))
369 end do
370 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
371 = this%send_buf(off + 1 : off + ndst)
372 end do
373#ifdef HAVE_COARRAY_EVENTS
374 else if (gs_caf_mode .eq. gs_caf_signal_event) then
375 ! Event mode shares one set of module-level event coarrays among
376 ! all instances and cannot disambiguate posts from concurrent gs
377 ! ops, so we must guarantee non-overlapping nbsend/nbwait windows.
378 if (gs_caf_event_in_use) then
379 call neko_error("Event-mode coarray gather-scatter does not " // &
380 "support overlapping gs ops on different instances")
381 end if
382 gs_caf_event_in_use = .true.
383
384 ! Wait for all receivers to have credited their buffers (skipped
385 ! on the first nbsend; there are no credits posted yet).
386 if (this%send_started) then
387 if (size(this%send_pe) .gt. 0) then
388 event wait(gs_caf_buf_ready_ev, until_count=size(this%send_pe))
389 end if
390 else
391 this%send_started = .true.
392 end if
393
394 do i = 1, size(this%send_pe)
395 dst = this%send_pe(i)
396 off = this%send_offset(i)
397 ndst = this%send_len(i)
398 dimg = this%send_img(i)
399 doff = this%dest_offset(i)
400 sp => this%send_dof(dst)%array()
401 do concurrent(j = 1:ndst)
402 this%send_buf(off + j) = u(sp(j))
403 end do
404 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
405 = this%send_buf(off + 1 : off + ndst)
406 ! event post is meant to act as an image-control statement
407 ! that establishes segment ordering with the matching event
408 ! wait, but real-world coarray runtimes can let a small event
409 ! message race past a still-in-flight RDMA put -- the
410 ! receiver's wait then completes before the data has landed.
411 ! sync memory forces the put to commit locally before the post.
412 sync memory
413 event post(gs_caf_data_ready_ev[dimg])
414 end do
415#endif
416 else
417 me_rank = this_image() - 1
418
419 ! Pack all peers up front so the subsequent network waits and
420 ! puts can overlap with each other rather than serialising
421 ! behind per-peer pack work.
422 do i = 1, size(this%send_pe)
423 dst = this%send_pe(i)
424 off = this%send_offset(i)
425 ndst = this%send_len(i)
426 sp => this%send_dof(dst)%array()
427 do concurrent(j = 1:ndst)
428 this%send_buf(off + j) = u(sp(j))
429 end do
430 end do
431
432 ! Back-pressure, put and signal per peer. With double-buffering
433 ! the half we are about to write last carried round
434 ! (send_count - 2), so we only need the receiver to have
435 ! unpacked through (send_count - 1).
436 do i = 1, size(this%send_pe)
437 off = this%send_offset(i)
438 ndst = this%send_len(i)
439 dimg = this%send_img(i)
440 doff = this%dest_offset(i)
441
442 do
443 call atomic_ref(flag, gs_caf_buf_ready(this%send_pe(i)))
444 if (int(flag) .ge. gs_caf_send_count(this%send_pe(i)) - 1) exit
445 end do
446
447 gs_caf_recv_buf(half_off + doff + 1 : half_off + doff + ndst)[dimg] &
448 = this%send_buf(off + 1 : off + ndst)
449
450 gs_caf_send_count(this%send_pe(i)) = &
451 gs_caf_send_count(this%send_pe(i)) + 1
452 call atomic_define(gs_caf_data_ready(me_rank)[dimg], &
453 int(gs_caf_send_count(this%send_pe(i)), atomic_int_kind))
454 end do
455 end if
456#else
457 call neko_error("Coarray Fortran support not built")
458#endif
459 end subroutine gs_nbsend_caf
460
463 subroutine gs_nbrecv_caf(this, tag)
464 class(gs_caf_t), intent(inout) :: this
465 integer, intent(in) :: tag
466 end subroutine gs_nbrecv_caf
467
472 subroutine gs_nbwait_caf(this, u, n, op, strm)
473 class(gs_caf_t), intent(inout) :: this
474 integer, intent(in) :: n
475 real(kind=rp), dimension(n), intent(inout) :: u
476 type(c_ptr), intent(inout) :: strm
477 integer :: op
478#ifdef HAVE_COARRAY
479 integer :: i, j, src, off, nsrc, half_off
480 integer, pointer :: sp(:)
481 integer(kind=atomic_int_kind) :: flag
482 integer :: me_rank
483
484 half_off = this%parity * gs_caf_buf_size
485
486 if (gs_caf_mode .eq. gs_caf_signal_sync) then
487 if (allocated(this%sync_img)) then
488 if (size(this%sync_img) .gt. 0) then
489 sync images(this%sync_img)
490 end if
491 end if
492#ifdef HAVE_COARRAY_EVENTS
493 else if (gs_caf_mode .eq. gs_caf_signal_event) then
494 if (size(this%recv_pe) .gt. 0) then
495 event wait(gs_caf_data_ready_ev, until_count=size(this%recv_pe))
496 end if
497#endif
498 else
499 ! Atomic mode: spin per-sender on data_ready until the expected
500 ! round count is observed.
501 do i = 1, size(this%recv_pe)
502 gs_caf_recv_count(this%recv_pe(i)) = &
503 gs_caf_recv_count(this%recv_pe(i)) + 1
504 do
505 call atomic_ref(flag, gs_caf_data_ready(this%recv_pe(i)))
506 if (int(flag) .ge. gs_caf_recv_count(this%recv_pe(i))) exit
507 end do
508 end do
509 end if
510
511 do i = 1, size(this%recv_pe)
512 src = this%recv_pe(i)
513 off = this%recv_offset(i)
514 nsrc = this%recv_len(i)
515 sp => this%recv_dof(src)%array()
516 select case (op)
517 case (gs_op_add)
518 !NEC$ IVDEP
519 do concurrent(j = 1:nsrc)
520 u(sp(j)) = u(sp(j)) + gs_caf_recv_buf(half_off + off + j)
521 end do
522 case (gs_op_mul)
523 !NEC$ IVDEP
524 do concurrent(j = 1:nsrc)
525 u(sp(j)) = u(sp(j)) * gs_caf_recv_buf(half_off + off + j)
526 end do
527 case (gs_op_min)
528 !NEC$ IVDEP
529 do concurrent(j = 1:nsrc)
530 u(sp(j)) = min(u(sp(j)), gs_caf_recv_buf(half_off + off + j))
531 end do
532 case (gs_op_max)
533 !NEC$ IVDEP
534 do concurrent(j = 1:nsrc)
535 u(sp(j)) = max(u(sp(j)), gs_caf_recv_buf(half_off + off + j))
536 end do
537 case default
538 call neko_error("Unknown operation in gs_nbwait_caf")
539 end select
540 end do
541
542 if (gs_caf_mode .eq. gs_caf_signal_atomic) then
543 ! Credit each sender that we have unpacked their slab so they
544 ! may proceed with their next round.
545 me_rank = this_image() - 1
546 do i = 1, size(this%recv_pe)
547 call atomic_define(gs_caf_buf_ready(me_rank)[this%recv_img(i)], &
548 int(gs_caf_recv_count(this%recv_pe(i)), atomic_int_kind))
549 end do
550#ifdef HAVE_COARRAY_EVENTS
551 else if (gs_caf_mode .eq. gs_caf_signal_event) then
552 do i = 1, size(this%recv_pe)
553 event post(gs_caf_buf_ready_ev[this%recv_img(i)])
554 end do
555 gs_caf_event_in_use = .false.
556#endif
557 end if
558
559 ! Flip the double-buffer parity for the next round.
560 this%parity = 1 - this%parity
561#else
562 call neko_error("Coarray Fortran support not built")
563#endif
564 end subroutine gs_nbwait_caf
565
570 subroutine gs_nbsend_vec_caf(this, u, n, nc, tag, deps, strm)
571 class(gs_caf_t), intent(inout) :: this
572 integer, intent(in) :: n, nc
573 real(kind=rp), dimension(nc*n), intent(inout) :: u
574 integer, intent(in) :: tag
575 type(c_ptr), intent(inout) :: deps
576 type(c_ptr), intent(inout) :: strm
577#ifdef HAVE_COARRAY
578 integer :: i, j, c, dst, off, dimg, ndst, doff, half_off
579 integer, pointer :: sp(:)
580 integer(kind=atomic_int_kind) :: flag
581 integer :: me_rank
582
583 half_off = this%parity * gs_caf_buf_size
584
585 if (gs_caf_mode .eq. gs_caf_signal_sync) then
586 do i = 1, size(this%send_pe)
587 dst = this%send_pe(i)
588 off = this%send_offset(i)
589 ndst = this%send_len(i)
590 dimg = this%send_img(i)
591 doff = this%dest_offset(i)
592 sp => this%send_dof(dst)%array()
593 do c = 1, nc
594 do concurrent(j = 1:ndst)
595 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
596 end do
597 end do
598 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
599 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
600 end do
601#ifdef HAVE_COARRAY_EVENTS
602 else if (gs_caf_mode .eq. gs_caf_signal_event) then
603 if (gs_caf_event_in_use) then
604 call neko_error("Event-mode coarray gather-scatter does not " // &
605 "support overlapping gs ops on different instances")
606 end if
607 gs_caf_event_in_use = .true.
608
609 if (this%send_started) then
610 if (size(this%send_pe) .gt. 0) then
611 event wait(gs_caf_buf_ready_ev, until_count=size(this%send_pe))
612 end if
613 else
614 this%send_started = .true.
615 end if
616
617 do i = 1, size(this%send_pe)
618 dst = this%send_pe(i)
619 off = this%send_offset(i)
620 ndst = this%send_len(i)
621 dimg = this%send_img(i)
622 doff = this%dest_offset(i)
623 sp => this%send_dof(dst)%array()
624 do c = 1, nc
625 do concurrent(j = 1:ndst)
626 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
627 end do
628 end do
629 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
630 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
631 sync memory
632 event post(gs_caf_data_ready_ev[dimg])
633 end do
634#endif
635 else
636 me_rank = this_image() - 1
637
638 do i = 1, size(this%send_pe)
639 dst = this%send_pe(i)
640 off = this%send_offset(i)
641 ndst = this%send_len(i)
642 sp => this%send_dof(dst)%array()
643 do c = 1, nc
644 do concurrent(j = 1:ndst)
645 this%send_buf(nc*off + (c-1)*ndst + j) = u((c-1)*n + sp(j))
646 end do
647 end do
648 end do
649
650 do i = 1, size(this%send_pe)
651 off = this%send_offset(i)
652 ndst = this%send_len(i)
653 dimg = this%send_img(i)
654 doff = this%dest_offset(i)
655
656 do
657 call atomic_ref(flag, gs_caf_buf_ready(this%send_pe(i)))
658 if (int(flag) .ge. gs_caf_send_count(this%send_pe(i)) - 1) exit
659 end do
660
661 gs_caf_recv_buf(half_off + nc*doff + 1 : half_off + nc*doff + nc*ndst) &
662 [dimg] = this%send_buf(nc*off + 1 : nc*off + nc*ndst)
663
664 gs_caf_send_count(this%send_pe(i)) = &
665 gs_caf_send_count(this%send_pe(i)) + 1
666 call atomic_define(gs_caf_data_ready(me_rank)[dimg], &
667 int(gs_caf_send_count(this%send_pe(i)), atomic_int_kind))
668 end do
669 end if
670#else
671 call neko_error("Coarray Fortran support not built")
672#endif
673 end subroutine gs_nbsend_vec_caf
674
676 subroutine gs_nbrecv_vec_caf(this, tag, nc)
677 class(gs_caf_t), intent(inout) :: this
678 integer, intent(in) :: tag, nc
679 end subroutine gs_nbrecv_vec_caf
680
682 subroutine gs_nbwait_vec_caf(this, u, n, nc, op, strm)
683 class(gs_caf_t), intent(inout) :: this
684 integer, intent(in) :: n, nc
685 real(kind=rp), dimension(nc*n), intent(inout) :: u
686 type(c_ptr), intent(inout) :: strm
687 integer :: op
688#ifdef HAVE_COARRAY
689 integer :: i, j, c, src, off, nsrc, half_off
690 integer, pointer :: sp(:)
691 integer(kind=atomic_int_kind) :: flag
692 integer :: me_rank
693
694 half_off = this%parity * gs_caf_buf_size
695
696 if (gs_caf_mode .eq. gs_caf_signal_sync) then
697 if (allocated(this%sync_img)) then
698 if (size(this%sync_img) .gt. 0) then
699 sync images(this%sync_img)
700 end if
701 end if
702#ifdef HAVE_COARRAY_EVENTS
703 else if (gs_caf_mode .eq. gs_caf_signal_event) then
704 if (size(this%recv_pe) .gt. 0) then
705 event wait(gs_caf_data_ready_ev, until_count=size(this%recv_pe))
706 end if
707#endif
708 else
709 do i = 1, size(this%recv_pe)
710 gs_caf_recv_count(this%recv_pe(i)) = &
711 gs_caf_recv_count(this%recv_pe(i)) + 1
712 do
713 call atomic_ref(flag, gs_caf_data_ready(this%recv_pe(i)))
714 if (int(flag) .ge. gs_caf_recv_count(this%recv_pe(i))) exit
715 end do
716 end do
717 end if
718
719 do i = 1, size(this%recv_pe)
720 src = this%recv_pe(i)
721 off = this%recv_offset(i)
722 nsrc = this%recv_len(i)
723 sp => this%recv_dof(src)%array()
724 select case (op)
725 case (gs_op_add)
726 do c = 1, nc
727 !NEC$ IVDEP
728 do concurrent(j = 1:nsrc)
729 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
730 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j)
731 end do
732 end do
733 case (gs_op_mul)
734 do c = 1, nc
735 !NEC$ IVDEP
736 do concurrent(j = 1:nsrc)
737 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
738 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j)
739 end do
740 end do
741 case (gs_op_min)
742 do c = 1, nc
743 !NEC$ IVDEP
744 do concurrent(j = 1:nsrc)
745 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
746 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j))
747 end do
748 end do
749 case (gs_op_max)
750 do c = 1, nc
751 !NEC$ IVDEP
752 do concurrent(j = 1:nsrc)
753 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
754 gs_caf_recv_buf(half_off + nc*off + (c-1)*nsrc + j))
755 end do
756 end do
757 case default
758 call neko_error("Unknown operation in gs_nbwait_vec_caf")
759 end select
760 end do
761
762 if (gs_caf_mode .eq. gs_caf_signal_atomic) then
763 me_rank = this_image() - 1
764 do i = 1, size(this%recv_pe)
765 call atomic_define(gs_caf_buf_ready(me_rank)[this%recv_img(i)], &
766 int(gs_caf_recv_count(this%recv_pe(i)), atomic_int_kind))
767 end do
768#ifdef HAVE_COARRAY_EVENTS
769 else if (gs_caf_mode .eq. gs_caf_signal_event) then
770 do i = 1, size(this%recv_pe)
771 event post(gs_caf_buf_ready_ev[this%recv_img(i)])
772 end do
773 gs_caf_event_in_use = .false.
774#endif
775 end if
776
777 this%parity = 1 - this%parity
778#else
779 call neko_error("Coarray Fortran support not built")
780#endif
781 end subroutine gs_nbwait_vec_caf
782
783end module gs_caf
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:61
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:464
subroutine gs_nbrecv_vec_caf(this, tag, nc)
No-op: senders push into the receiver's buffer.
Definition gs_caf.F90:677
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:345
subroutine gs_caf_init(this, send_pe, recv_pe)
Initialise Coarray Fortran based communication method.
Definition gs_caf.F90:166
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:473
integer, parameter, public gs_caf_signal_event
Definition gs_caf.F90:55
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:571
integer, parameter, public gs_caf_signal_atomic
Definition gs_caf.F90:54
subroutine gs_caf_free(this)
Deallocate Coarray Fortran based communication method. The shared module-level recv coarray is intent...
Definition gs_caf.F90:322
integer, parameter, public gs_caf_signal_sync
Definition gs_caf.F90:53
subroutine gs_nbwait_vec_caf(this, u, n, nc, op, strm)
Fused nc-component wait/reduce for the coarray backend.
Definition gs_caf.F90:683
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:129
Gather-scatter communication method.
Definition gs_comm.f90:52
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40