Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_shmem.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!
35 use num_types, only : rp, c_rp, i8
36 use gs_comm, only : gs_comm_t, gs_vec_nc
38 use stack, only : stack_i4_t
39 use comm, only : neko_comm, pe_rank, pe_size
40 use mpi_f08, only : mpi_allreduce, mpi_alltoall, mpi_integer, mpi_max
41 use utils, only : neko_error
42#ifdef HAVE_OPENSHMEM
48#endif
49 use, intrinsic :: iso_c_binding, only : c_ptr, c_null_ptr, c_loc, &
50 c_f_pointer, c_associated, c_sizeof, c_size_t, c_int64_t, c_int
51 implicit none
52 private
53
60 logical :: gs_shmem_thread_multiple = .false.
61
65#ifdef HAVE_OPENSHMEM
66 logical, parameter, public :: gs_shmem_avail = .true.
67#else
68 logical, parameter, public :: gs_shmem_avail = .false.
69#endif
70
74 integer, allocatable :: ndofs(:)
76 integer, allocatable :: offset(:)
79 integer, allocatable :: remote_offset(:)
81 integer :: total = 0
83 integer :: max_total = 0
85 type(c_ptr) :: buf_ptr = c_null_ptr
86 contains
87 procedure, pass(this) :: init => gs_shmem_buf_init
88 procedure, pass(this) :: free => gs_shmem_buf_free
89 end type gs_shmem_buf_t
90
116 type, public, extends(gs_comm_t) :: gs_shmem_t
117 type(gs_shmem_buf_t) :: send_buf
118 type(gs_shmem_buf_t) :: recv_buf
119 ! Symmetric data-arrival signals; data_signals[r] is set to `iter`
120 ! by PE r via shmem_putmem_signal_nbi when it has put data into
121 ! our recv buffer. Sized to pe_size on every PE.
122 type(c_ptr) :: data_signals_ptr = c_null_ptr
123 ! Symmetric ack signals; ack_signals[r] is set to `iter` by PE r
124 ! via shmem_uint64_atomic_set after it has consumed our most
125 ! recent put. Sized to pe_size on every PE.
126 type(c_ptr) :: ack_signals_ptr = c_null_ptr
127 ! Monotonically increasing iteration counter; sender writes this
128 ! value into the receiver's data signal slot via SHMEM_SIGNAL_SET,
129 ! and the receiver writes the same value into the sender's ack
130 ! slot after consumption.
131 integer(kind=i8) :: iter = 0
132 contains
133 procedure, pass(this) :: init => gs_shmem_init
134 procedure, pass(this) :: free => gs_shmem_free
135 procedure, pass(this) :: nbsend => gs_shmem_nbsend
136 procedure, pass(this) :: nbrecv => gs_shmem_nbrecv
137 procedure, pass(this) :: nbwait => gs_shmem_nbwait
138 procedure, pass(this) :: nbsend_vec => gs_shmem_nbsend_vec
139 procedure, pass(this) :: nbrecv_vec => gs_shmem_nbrecv_vec
140 procedure, pass(this) :: nbwait_vec => gs_shmem_nbwait_vec
141 end type gs_shmem_t
142
143contains
144
149 subroutine gs_shmem_buf_init(this, pe_order, dof_stack)
150 class(gs_shmem_buf_t), intent(inout) :: this
151 integer, intent(in) :: pe_order(:)
152 type(stack_i4_t), intent(inout) :: dof_stack(0:)
153 integer :: i, ierr, n
154 integer(c_size_t) :: sz
155 real(c_rp) :: rp_dummy
156#ifndef HAVE_OPENSHMEM
157 call neko_error('Neko was not built with OpenSHMEM support')
158#else
159
160 n = size(pe_order)
161
162 allocate(this%ndofs(n))
163 allocate(this%offset(n))
164 allocate(this%remote_offset(n))
165
166 do i = 1, n
167 this%remote_offset(i) = -1
168 end do
169
170 this%total = 0
171 do i = 1, n
172 this%ndofs(i) = dof_stack(pe_order(i))%size()
173 this%offset(i) = this%total
174 this%total = this%total + this%ndofs(i)
175 end do
176
177 ! OpenSHMEM symmetric memory must be allocated with the same size on
178 ! every PE.
179 call mpi_allreduce(this%total, this%max_total, 1, mpi_integer, mpi_max, &
180 neko_comm, ierr)
181
182 ! Sized for up to GS_VEC_NC components so the fused vector path can
183 ! reuse the same symmetric buffer. Peer i's slab starts at
184 ! GS_VEC_NC*offset(i) for EVERY round type: a scalar round uses its
185 ! first ndofs(i) entries, a fused nc-component round its first
186 ! nc*ndofs(i). The slabs have to be disjoint across peers regardless
187 ! of the round type because the per-peer ack only protects the acking
188 ! peer's own slab: a peer that has been acked may already be putting
189 ! its next round while slabs from slower peers are still being
190 ! reduced. Scalar rounds at 1*offset and fused rounds at nc*offset,
191 ! as this backend first did, let peer i's fused slab overlap peer j's
192 ! scalar slab and corrupted the halo whenever the two round types
193 ! alternated on one instance (fluid_pnpn does so every step).
194 sz = c_sizeof(rp_dummy) * int(max(gs_vec_nc*this%max_total, 1), c_size_t)
195 this%buf_ptr = shmem_malloc(sz)
196 if (.not. c_associated(this%buf_ptr)) then
197 call neko_error('shmem_malloc failed for gs_shmem buffer')
198 end if
199#endif
200 end subroutine gs_shmem_buf_init
201
203 subroutine gs_shmem_buf_free(this)
204 class(gs_shmem_buf_t), intent(inout) :: this
205
206 if (allocated(this%ndofs)) deallocate(this%ndofs)
207 if (allocated(this%offset)) deallocate(this%offset)
208 if (allocated(this%remote_offset)) deallocate(this%remote_offset)
209
210#ifdef HAVE_OPENSHMEM
211 if (c_associated(this%buf_ptr)) then
212 ! shmem_free is collective; gs_shmem_free issues a barrier before
213 ! tearing down buffers.
214 call shmem_free(this%buf_ptr)
215 end if
216#endif
217 this%buf_ptr = c_null_ptr
218 this%total = 0
219 this%max_total = 0
220
221 end subroutine gs_shmem_buf_free
222
224 subroutine gs_shmem_init(this, send_pe, recv_pe)
225 class(gs_shmem_t), intent(inout) :: this
226 type(stack_i4_t), intent(inout) :: send_pe
227 type(stack_i4_t), intent(inout) :: recv_pe
228 integer :: i, ierr
229 integer(c_size_t) :: i64_size
230 integer(c_int64_t) :: i64_dummy
231 integer, allocatable :: local_offsets(:), remote_offsets(:)
232#ifdef HAVE_OPENSHMEM
233 integer(c_int) :: thread_level
234#endif
235#ifndef HAVE_OPENSHMEM
236 call neko_error('Neko was not built with OpenSHMEM support')
237#else
238
239 ! No self-free here: gs_schedule has just filled send_dof/recv_dof and
240 ! gs_shmem_free would deallocate them again, leaving the buffer setup
241 ! below reading from a dangling descriptor. Like every other comm
242 ! backend, init assumes a freshly allocated object.
243 call this%init_order(send_pe, recv_pe)
244
245 ! Decide once whether the OpenSHMEM library tolerates concurrent calls
246 ! from several OpenMP threads; the send path branches on it.
247 call shmem_query_thread(thread_level)
249
250 call this%send_buf%init(this%send_pe, this%send_dof)
251 call this%recv_buf%init(this%recv_pe, this%recv_dof)
252
253 ! Allocate the per-rank symmetric signal arrays. Size pe_size on
254 ! every PE -> no Allreduce needed and no slot handshake required;
255 ! every PE writes/reads at offset = remote PE's rank.
256 i64_size = c_sizeof(i64_dummy)
257 this%data_signals_ptr = shmem_calloc(int(pe_size, c_size_t), i64_size)
258 if (.not. c_associated(this%data_signals_ptr)) then
259 call neko_error('shmem_calloc failed for gs_shmem data signals')
260 end if
261 this%ack_signals_ptr = shmem_calloc(int(pe_size, c_size_t), i64_size)
262 if (.not. c_associated(this%ack_signals_ptr)) then
263 call neko_error('shmem_calloc failed for gs_shmem ack signals')
264 end if
265
266 ! Offset exchange via Alltoall. send_pe and recv_pe are built in
267 ! gs_schedule with a shifted-modulo neighbor pattern; pairwise
268 ! Isend/Irecv keyed by neighbor rank turned out to deadlock at
269 ! certain PE counts. Alltoall is a single collective call that
270 ! cannot mismatch, and the payload is just pe_size ints per PE.
271 allocate(local_offsets(0:pe_size - 1))
272 allocate(remote_offsets(0:pe_size - 1))
273 local_offsets = -1
274 do i = 1, size(this%recv_pe)
275 local_offsets(this%recv_pe(i)) = this%recv_buf%offset(i)
276 end do
277 call mpi_alltoall(local_offsets, 1, mpi_integer, &
278 remote_offsets, 1, mpi_integer, neko_comm, ierr)
279 do i = 1, size(this%send_pe)
280 this%send_buf%remote_offset(i) = remote_offsets(this%send_pe(i))
281 end do
282 deallocate(local_offsets)
283 deallocate(remote_offsets)
284
285 this%iter = 0
286 this%vec_supported = .true.
287 ! The vector slabs are part of the symmetric/registered allocation
288 ! made above, which every rank has to take part in, so they cannot
289 ! be deferred to the first fused exchange: a rank with no shared
290 ! dofs never reaches it. See gs_comm_t%vec_ready.
291 this%vec_ready = .true.
292
293 ! Ensure all PEs have completed symmetric allocation before any
294 ! one-sided communication is issued.
295 call shmem_barrier_all()
296#endif
297 end subroutine gs_shmem_init
298
300 subroutine gs_shmem_free(this)
301 class(gs_shmem_t), intent(inout) :: this
302
303#ifdef HAVE_OPENSHMEM
304 ! shmem_free is collective; synchronize first to ensure no in-flight
305 ! puts target the buffers we are about to release.
306 call shmem_barrier_all()
307
308 if (c_associated(this%data_signals_ptr)) then
309 call shmem_free(this%data_signals_ptr)
310 end if
311 if (c_associated(this%ack_signals_ptr)) then
312 call shmem_free(this%ack_signals_ptr)
313 end if
314 this%data_signals_ptr = c_null_ptr
315 this%ack_signals_ptr = c_null_ptr
316 this%iter = 0
317#endif
318
319 call this%send_buf%free()
320 call this%recv_buf%free()
321
322 call this%free_order()
323 call this%free_dofs()
324
325 end subroutine gs_shmem_free
326
332 subroutine gs_shmem_nbsend(this, u, n, tag, deps, strm)
333 class(gs_shmem_t), intent(inout) :: this
334 integer, intent(in) :: n
335 real(kind=rp), dimension(n), intent(inout) :: u
336 integer, intent(in) :: tag
337 type(c_ptr), intent(inout) :: deps
338 type(c_ptr), intent(inout) :: strm
339 integer :: i, j, dst, base, ndst
340 integer(c_size_t) :: nbytes
341 integer , pointer :: sp(:)
342 real(kind=rp), pointer :: send_data(:), recv_data(:)
343 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
344 real(c_rp) :: rp_dummy
345#ifdef HAVE_OPENSHMEM
346
347 ! Entered from inside the gs_op_vector OpenMP parallel region. Every
348 ! thread binds its own pointers to the symmetric buffers (the c_f_pointer
349 ! targets are private locals), but the iteration counter is shared state
350 ! and must be bumped exactly once per round.
351 !$omp master
352 ! Each gs op gets a fresh signal value so receivers can distinguish
353 ! puts from one call vs. the next.
354 this%iter = this%iter + 1
355 !$omp end master
356
357 call c_f_pointer(this%send_buf%buf_ptr, send_data, &
358 [max(this%send_buf%max_total, 1)])
359 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
360 [max(gs_vec_nc*this%recv_buf%max_total, 1)])
361 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
362 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
363
364 ! Publish the new iter before anyone reads it below.
365 !$omp barrier
366
368 ! Each thread drives the whole protocol for its share of the peers,
369 ! keeping the per-peer wait/pack/put order of the serial code.
370 !$omp do
371 do i = 1, size(this%send_pe)
372 dst = this%send_pe(i)
373
374 ! Wait for dst to have consumed our previous round's data before
375 ! we overwrite their recv buffer. ack_signals[dst] is calloc'd
376 ! to 0 and dst writes the iter value (via atomic_set) after
377 ! consumption, so for iter == 1 the wait succeeds immediately.
378 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
379 shmem_cmp_ge, this%iter - 1_8)
380
381 sp => this%send_dof(dst)%array()
382 base = this%send_buf%offset(i)
383 ndst = this%send_buf%ndofs(i)
384 do concurrent(j = 1:ndst)
385 send_data(base + j) = u(sp(j))
386 end do
387
388 nbytes = int(ndst, c_size_t) * c_sizeof(rp_dummy)
389 ! Put data + signal dst's data_signals[my_rank].
391 c_loc(recv_data(gs_vec_nc*this%send_buf%remote_offset(i) + 1)), &
392 c_loc(send_data(base + 1)), &
393 nbytes, &
394 c_loc(data_signals(pe_rank + 1)), &
395 this%iter, shmem_signal_set, dst)
396 end do
397 !$omp end do
398 else
399 ! Funnelled: every SHMEM call is left to the master and only the pack
400 ! is work-shared, one peer at a time. The per-peer wait/pack/put order
401 ! of the serial code is kept -- the ack is what makes a slab safe to
402 ! repack as well, since it means the previous round's non-blocking put
403 ! has been consumed and no longer reads our send buffer. The barrier
404 ! after the ack wait releases the team into the pack, and the implicit
405 ! barrier at end do completes the slab before the master puts it.
406 do i = 1, size(this%send_pe)
407 dst = this%send_pe(i)
408 base = this%send_buf%offset(i)
409 ndst = this%send_buf%ndofs(i)
410
411 !$omp master
412 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
413 shmem_cmp_ge, this%iter - 1_8)
414 !$omp end master
415 !$omp barrier
416
417 sp => this%send_dof(dst)%array()
418 !OCL NORECURRENCE, NOVREC, NOALIAS
419 !DIR$ CONCURRENT
420 !DIR$ IVDEP
421 !GCC$ ivdep
422 !NEC$ IVDEP
423 !$omp do
424 do j = 1, ndst
425 send_data(base + j) = u(sp(j))
426 end do
427 !$omp end do
428
429 !$omp master
430 nbytes = int(ndst, c_size_t) * c_sizeof(rp_dummy)
432 c_loc(recv_data(gs_vec_nc*this%send_buf%remote_offset(i) + 1)), &
433 c_loc(send_data(base + 1)), &
434 nbytes, &
435 c_loc(data_signals(pe_rank + 1)), &
436 this%iter, shmem_signal_set, dst)
437 !$omp end master
438 end do
439 !$omp barrier
440 end if
441#endif
442 end subroutine gs_shmem_nbsend
443
445 subroutine gs_shmem_nbrecv(this, tag)
446 class(gs_shmem_t), intent(inout) :: this
447 integer, intent(in) :: tag
448
449 end subroutine gs_shmem_nbrecv
450
454 subroutine gs_shmem_nbwait(this, u, n, op, strm)
455 class(gs_shmem_t), intent(inout) :: this
456 integer, intent(in) :: n
457 real(kind=rp), dimension(n), intent(inout) :: u
458 type(c_ptr), intent(inout) :: strm
459 integer :: op
460 integer :: i, j, src, base, nsrc
461 integer(c_int64_t) :: dummy
462 integer , pointer :: sp(:)
463 real(kind=rp), pointer :: recv_data(:)
464 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
465#ifdef HAVE_OPENSHMEM
466
467 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
468 [max(gs_vec_nc*this%recv_buf%max_total, 1)])
469 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
470 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
471
472 ! The loop over peers stays serial even when the library is thread
473 ! multiple: a dof shared by 3+ ranks appears in several recv_dof lists,
474 ! so reducing two slabs concurrently would race on that dof. The master
475 ! awaits and acks each slab in turn and the team reduces it, taking the
476 ! parallelism within the slab instead.
477 do i = 1, size(this%recv_pe)
478 src = this%recv_pe(i)
479 ! Slab at the GS_VEC_NC stride whatever the round type, see buf_init
480 base = gs_vec_nc*this%recv_buf%offset(i)
481 nsrc = this%recv_buf%ndofs(i)
482
483 ! Wait for data from src; data_signals[src] is set by src's put.
484 !$omp master
485 dummy = shmem_signal_wait_until(c_loc(data_signals(src + 1)), &
486 shmem_cmp_ge, this%iter)
487 !$omp end master
488 !$omp barrier
489
490 sp => this%recv_dof(src)%array()
491 select case (op)
492 case (gs_op_add)
493 !OCL NORECURRENCE, NOVREC, NOALIAS
494 !DIR$ CONCURRENT
495 !DIR$ IVDEP
496 !GCC$ ivdep
497 !NEC$ IVDEP
498 !$omp do
499 do j = 1, nsrc
500 u(sp(j)) = u(sp(j)) + recv_data(base + j)
501 end do
502 !$omp end do
503 case (gs_op_mul)
504 !OCL NORECURRENCE, NOVREC, NOALIAS
505 !DIR$ CONCURRENT
506 !DIR$ IVDEP
507 !GCC$ ivdep
508 !NEC$ IVDEP
509 !$omp do
510 do j = 1, nsrc
511 u(sp(j)) = u(sp(j)) * recv_data(base + j)
512 end do
513 !$omp end do
514 case (gs_op_min)
515 !OCL NORECURRENCE, NOVREC, NOALIAS
516 !DIR$ CONCURRENT
517 !DIR$ IVDEP
518 !GCC$ ivdep
519 !NEC$ IVDEP
520 !$omp do
521 do j = 1, nsrc
522 u(sp(j)) = min(u(sp(j)), recv_data(base + j))
523 end do
524 !$omp end do
525 case (gs_op_max)
526 !OCL NORECURRENCE, NOVREC, NOALIAS
527 !DIR$ CONCURRENT
528 !DIR$ IVDEP
529 !GCC$ ivdep
530 !NEC$ IVDEP
531 !$omp do
532 do j = 1, nsrc
533 u(sp(j)) = max(u(sp(j)), recv_data(base + j))
534 end do
535 !$omp end do
536 case default
537 call neko_error("Unknown operation in gs_nbwait_shmem")
538 end select
539
540 ! Tell src we are done with this round's buffer so they may
541 ! overwrite it on the next round. We set our own rank's slot
542 ! in src's ack_signals; src waits there on the next nbsend. The
543 ! implicit barrier at the end do above guarantees the whole team is
544 ! finished with the slab before it is released.
545 !$omp master
547 c_loc(ack_signals(pe_rank + 1)), &
548 this%iter, src)
549 !$omp end master
550 end do
551#endif
552 end subroutine gs_shmem_nbwait
553
558 subroutine gs_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
559 class(gs_shmem_t), intent(inout) :: this
560 integer, intent(in) :: n, nc
561 real(kind=rp), dimension(nc*n), intent(inout) :: u
562 integer, intent(in) :: tag
563 type(c_ptr), intent(inout) :: deps
564 type(c_ptr), intent(inout) :: strm
565 integer :: i, j, c, dst, base, ndst
566 integer(c_size_t) :: nbytes
567 integer, pointer :: sp(:)
568 real(kind=rp), pointer :: send_data(:), recv_data(:)
569 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
570 real(c_rp) :: rp_dummy
571#ifdef HAVE_OPENSHMEM
572
573 !$omp master
574 this%iter = this%iter + 1
575 !$omp end master
576
577 call c_f_pointer(this%send_buf%buf_ptr, send_data, &
578 [max(nc*this%send_buf%max_total, 1)])
579 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
580 [max(gs_vec_nc*this%recv_buf%max_total, 1)])
581 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
582 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
583
584 !$omp barrier
585
586 ! Same threading split as the scalar path; see gs_shmem_nbsend.
588 !$omp do
589 do i = 1, size(this%send_pe)
590 dst = this%send_pe(i)
591
592 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
593 shmem_cmp_ge, this%iter - 1_8)
594
595 sp => this%send_dof(dst)%array()
596 base = this%send_buf%offset(i)
597 ndst = this%send_buf%ndofs(i)
598 do c = 1, nc
599 do concurrent(j = 1:ndst)
600 send_data(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
601 end do
602 end do
603
604 nbytes = int(nc*ndst, c_size_t) * c_sizeof(rp_dummy)
606 c_loc(recv_data(gs_vec_nc*this%send_buf%remote_offset(i) + 1)), &
607 c_loc(send_data(nc*base + 1)), &
608 nbytes, &
609 c_loc(data_signals(pe_rank + 1)), &
610 this%iter, shmem_signal_set, dst)
611 end do
612 !$omp end do
613 else
614 do i = 1, size(this%send_pe)
615 dst = this%send_pe(i)
616 base = this%send_buf%offset(i)
617 ndst = this%send_buf%ndofs(i)
618
619 !$omp master
620 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
621 shmem_cmp_ge, this%iter - 1_8)
622 !$omp end master
623 !$omp barrier
624
625 sp => this%send_dof(dst)%array()
626 !$omp do
627 do j = 1, ndst
628 do c = 1, nc
629 send_data(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
630 end do
631 end do
632 !$omp end do
633
634 !$omp master
635 nbytes = int(nc*ndst, c_size_t) * c_sizeof(rp_dummy)
637 c_loc(recv_data(gs_vec_nc*this%send_buf%remote_offset(i) + 1)), &
638 c_loc(send_data(nc*base + 1)), &
639 nbytes, &
640 c_loc(data_signals(pe_rank + 1)), &
641 this%iter, shmem_signal_set, dst)
642 !$omp end master
643 end do
644 !$omp barrier
645 end if
646#endif
647 end subroutine gs_shmem_nbsend_vec
648
650 subroutine gs_shmem_nbrecv_vec(this, tag, nc)
651 class(gs_shmem_t), intent(inout) :: this
652 integer, intent(in) :: tag, nc
653 end subroutine gs_shmem_nbrecv_vec
654
657 subroutine gs_shmem_nbwait_vec(this, u, n, nc, op, strm)
658 class(gs_shmem_t), intent(inout) :: this
659 integer, intent(in) :: n, nc
660 real(kind=rp), dimension(nc*n), intent(inout) :: u
661 type(c_ptr), intent(inout) :: strm
662 integer :: op
663 integer :: i, j, c, src, base, nsrc
664 integer(c_int64_t) :: dummy
665 integer, pointer :: sp(:)
666 real(kind=rp), pointer :: recv_data(:)
667 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
668#ifdef HAVE_OPENSHMEM
669
670 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
671 [max(gs_vec_nc*this%recv_buf%max_total, 1)])
672 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
673 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
674
675 ! Serial over peers (a dof shared by 3+ ranks appears in several recv
676 ! lists); parallelism is taken within each slab. See gs_shmem_nbwait.
677 do i = 1, size(this%recv_pe)
678 src = this%recv_pe(i)
679 ! Slab at the GS_VEC_NC stride whatever nc is, see buf_init
680 base = gs_vec_nc*this%recv_buf%offset(i)
681 nsrc = this%recv_buf%ndofs(i)
682
683 !$omp master
684 dummy = shmem_signal_wait_until(c_loc(data_signals(src + 1)), &
685 shmem_cmp_ge, this%iter)
686 !$omp end master
687 !$omp barrier
688
689 sp => this%recv_dof(src)%array()
690 select case (op)
691 case (gs_op_add)
692 !$omp do
693 do j = 1, nsrc
694 do c = 1, nc
695 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
696 recv_data(base + (c-1)*nsrc + j)
697 end do
698 end do
699 !$omp end do
700 case (gs_op_mul)
701 !$omp do
702 do j = 1, nsrc
703 do c = 1, nc
704 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
705 recv_data(base + (c-1)*nsrc + j)
706 end do
707 end do
708 !$omp end do
709 case (gs_op_min)
710 !$omp do
711 do j = 1, nsrc
712 do c = 1, nc
713 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
714 recv_data(base + (c-1)*nsrc + j))
715 end do
716 end do
717 !$omp end do
718 case (gs_op_max)
719 !$omp do
720 do j = 1, nsrc
721 do c = 1, nc
722 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
723 recv_data(base + (c-1)*nsrc + j))
724 end do
725 end do
726 !$omp end do
727 case default
728 call neko_error("Unknown operation in gs_shmem_nbwait_vec")
729 end select
730
731 !$omp master
733 c_loc(ack_signals(pe_rank + 1)), &
734 this%iter, src)
735 !$omp end master
736 end do
737#endif
738 end subroutine gs_shmem_nbwait_vec
739
740end module gs_shmem
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:62
integer, public pe_rank
MPI rank.
Definition comm.F90:59
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
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
Defines OpenSHMEM gather-scatter communication.
Definition gs_shmem.F90:34
subroutine gs_shmem_buf_init(this, pe_order, dof_stack)
Allocate symmetric memory and per-neighbor bookkeeping for one direction of communication.
Definition gs_shmem.F90:150
subroutine gs_shmem_nbrecv_vec(this, tag, nc)
No-op: receives are completed via remote put-with-signal.
Definition gs_shmem.F90:651
logical, parameter, public gs_shmem_avail
Whether a native OpenSHMEM library was built into this Neko (–with-openshmem). Lets callers (e....
Definition gs_shmem.F90:68
subroutine gs_shmem_init(this, send_pe, recv_pe)
Initialise OpenSHMEM based communication method.
Definition gs_shmem.F90:225
subroutine gs_shmem_nbwait_vec(this, u, n, nc, op, strm)
Fused nc-component wait/reduce: per peer, wait on the data signal and reduce nc component blocks into...
Definition gs_shmem.F90:658
subroutine gs_shmem_nbsend(this, u, n, tag, deps, strm)
Pack the gathered shared dofs into the symmetric send buffer and issue non-blocking puts with signali...
Definition gs_shmem.F90:333
subroutine gs_shmem_buf_free(this)
Release symmetric memory and bookkeeping.
Definition gs_shmem.F90:204
subroutine gs_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
Fused nc-component send: pack nc contiguous component blocks per peer slab and put nc*ndofs reals wit...
Definition gs_shmem.F90:559
subroutine gs_shmem_free(this)
Deallocate OpenSHMEM based communication method.
Definition gs_shmem.F90:301
subroutine gs_shmem_nbrecv(this, tag)
No-op: receives are completed via remote put-with-signal.
Definition gs_shmem.F90:446
logical gs_shmem_thread_multiple
True when the OpenSHMEM library provides SHMEM_THREAD_MULTIPLE. The send path then lets each OpenMP t...
Definition gs_shmem.F90:60
subroutine gs_shmem_nbwait(this, u, n, op, strm)
Wait per-neighbor for the signal indicating that data has landed, apply the gather-scatter operation ...
Definition gs_shmem.F90:455
integer, parameter, public i8
Definition num_types.f90:7
integer, parameter, public sp
Definition num_types.f90:8
integer, parameter, public c_rp
Definition num_types.f90:15
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Fortran bindings to SHMEM's C API.
Definition shmem.F90:34
@ shmem_signal_set
Definition shmem.F90:79
@ shmem_thread_multiple
Definition shmem.F90:57
@ shmem_cmp_ge
Definition shmem.F90:50
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:53
Symmetric buffer for one direction of OpenSHMEM communication.
Definition gs_shmem.F90:72
Gather-scatter communication using OpenSHMEM one-sided puts with per-rank signaling for completion (O...
Definition gs_shmem.F90:116
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40