Neko 1.99.7
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; the scalar path uses the first
184 ! max_total elements.
185 sz = c_sizeof(rp_dummy) * int(max(gs_vec_nc*this%max_total, 1), c_size_t)
186 this%buf_ptr = shmem_malloc(sz)
187 if (.not. c_associated(this%buf_ptr)) then
188 call neko_error('shmem_malloc failed for gs_shmem buffer')
189 end if
190#endif
191 end subroutine gs_shmem_buf_init
192
194 subroutine gs_shmem_buf_free(this)
195 class(gs_shmem_buf_t), intent(inout) :: this
196
197 if (allocated(this%ndofs)) deallocate(this%ndofs)
198 if (allocated(this%offset)) deallocate(this%offset)
199 if (allocated(this%remote_offset)) deallocate(this%remote_offset)
200
201#ifdef HAVE_OPENSHMEM
202 if (c_associated(this%buf_ptr)) then
203 ! shmem_free is collective; gs_shmem_free issues a barrier before
204 ! tearing down buffers.
205 call shmem_free(this%buf_ptr)
206 end if
207#endif
208 this%buf_ptr = c_null_ptr
209 this%total = 0
210 this%max_total = 0
211
212 end subroutine gs_shmem_buf_free
213
215 subroutine gs_shmem_init(this, send_pe, recv_pe)
216 class(gs_shmem_t), intent(inout) :: this
217 type(stack_i4_t), intent(inout) :: send_pe
218 type(stack_i4_t), intent(inout) :: recv_pe
219 integer :: i, ierr
220 integer(c_size_t) :: i64_size
221 integer(c_int64_t) :: i64_dummy
222 integer, allocatable :: local_offsets(:), remote_offsets(:)
223#ifdef HAVE_OPENSHMEM
224 integer(c_int) :: thread_level
225#endif
226#ifndef HAVE_OPENSHMEM
227 call neko_error('Neko was not built with OpenSHMEM support')
228#else
229
230 ! No self-free here: gs_schedule has just filled send_dof/recv_dof and
231 ! gs_shmem_free would deallocate them again, leaving the buffer setup
232 ! below reading from a dangling descriptor. Like every other comm
233 ! backend, init assumes a freshly allocated object.
234 call this%init_order(send_pe, recv_pe)
235
236 ! Decide once whether the OpenSHMEM library tolerates concurrent calls
237 ! from several OpenMP threads; the send path branches on it.
238 call shmem_query_thread(thread_level)
240
241 call this%send_buf%init(this%send_pe, this%send_dof)
242 call this%recv_buf%init(this%recv_pe, this%recv_dof)
243
244 ! Allocate the per-rank symmetric signal arrays. Size pe_size on
245 ! every PE -> no Allreduce needed and no slot handshake required;
246 ! every PE writes/reads at offset = remote PE's rank.
247 i64_size = c_sizeof(i64_dummy)
248 this%data_signals_ptr = shmem_calloc(int(pe_size, c_size_t), i64_size)
249 if (.not. c_associated(this%data_signals_ptr)) then
250 call neko_error('shmem_calloc failed for gs_shmem data signals')
251 end if
252 this%ack_signals_ptr = shmem_calloc(int(pe_size, c_size_t), i64_size)
253 if (.not. c_associated(this%ack_signals_ptr)) then
254 call neko_error('shmem_calloc failed for gs_shmem ack signals')
255 end if
256
257 ! Offset exchange via Alltoall. send_pe and recv_pe are built in
258 ! gs_schedule with a shifted-modulo neighbor pattern; pairwise
259 ! Isend/Irecv keyed by neighbor rank turned out to deadlock at
260 ! certain PE counts. Alltoall is a single collective call that
261 ! cannot mismatch, and the payload is just pe_size ints per PE.
262 allocate(local_offsets(0:pe_size - 1))
263 allocate(remote_offsets(0:pe_size - 1))
264 local_offsets = -1
265 do i = 1, size(this%recv_pe)
266 local_offsets(this%recv_pe(i)) = this%recv_buf%offset(i)
267 end do
268 call mpi_alltoall(local_offsets, 1, mpi_integer, &
269 remote_offsets, 1, mpi_integer, neko_comm, ierr)
270 do i = 1, size(this%send_pe)
271 this%send_buf%remote_offset(i) = remote_offsets(this%send_pe(i))
272 end do
273 deallocate(local_offsets)
274 deallocate(remote_offsets)
275
276 this%iter = 0
277 this%vec_supported = .true.
278
279 ! Ensure all PEs have completed symmetric allocation before any
280 ! one-sided communication is issued.
281 call shmem_barrier_all()
282#endif
283 end subroutine gs_shmem_init
284
286 subroutine gs_shmem_free(this)
287 class(gs_shmem_t), intent(inout) :: this
288
289#ifdef HAVE_OPENSHMEM
290 ! shmem_free is collective; synchronize first to ensure no in-flight
291 ! puts target the buffers we are about to release.
292 call shmem_barrier_all()
293
294 if (c_associated(this%data_signals_ptr)) then
295 call shmem_free(this%data_signals_ptr)
296 end if
297 if (c_associated(this%ack_signals_ptr)) then
298 call shmem_free(this%ack_signals_ptr)
299 end if
300 this%data_signals_ptr = c_null_ptr
301 this%ack_signals_ptr = c_null_ptr
302 this%iter = 0
303#endif
304
305 call this%send_buf%free()
306 call this%recv_buf%free()
307
308 call this%free_order()
309 call this%free_dofs()
310
311 end subroutine gs_shmem_free
312
318 subroutine gs_shmem_nbsend(this, u, n, tag, deps, strm)
319 class(gs_shmem_t), intent(inout) :: this
320 integer, intent(in) :: n
321 real(kind=rp), dimension(n), intent(inout) :: u
322 integer, intent(in) :: tag
323 type(c_ptr), intent(inout) :: deps
324 type(c_ptr), intent(inout) :: strm
325 integer :: i, j, dst, base, ndst
326 integer(c_size_t) :: nbytes
327 integer , pointer :: sp(:)
328 real(kind=rp), pointer :: send_data(:), recv_data(:)
329 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
330 real(c_rp) :: rp_dummy
331#ifdef HAVE_OPENSHMEM
332
333 ! Entered from inside the gs_op_vector OpenMP parallel region. Every
334 ! thread binds its own pointers to the symmetric buffers (the c_f_pointer
335 ! targets are private locals), but the iteration counter is shared state
336 ! and must be bumped exactly once per round.
337 !$omp master
338 ! Each gs op gets a fresh signal value so receivers can distinguish
339 ! puts from one call vs. the next.
340 this%iter = this%iter + 1
341 !$omp end master
342
343 call c_f_pointer(this%send_buf%buf_ptr, send_data, &
344 [max(this%send_buf%max_total, 1)])
345 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
346 [max(this%recv_buf%max_total, 1)])
347 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
348 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
349
350 ! Publish the new iter before anyone reads it below.
351 !$omp barrier
352
354 ! Each thread drives the whole protocol for its share of the peers,
355 ! keeping the per-peer wait/pack/put order of the serial code.
356 !$omp do
357 do i = 1, size(this%send_pe)
358 dst = this%send_pe(i)
359
360 ! Wait for dst to have consumed our previous round's data before
361 ! we overwrite their recv buffer. ack_signals[dst] is calloc'd
362 ! to 0 and dst writes the iter value (via atomic_set) after
363 ! consumption, so for iter == 1 the wait succeeds immediately.
364 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
365 shmem_cmp_ge, this%iter - 1_8)
366
367 sp => this%send_dof(dst)%array()
368 base = this%send_buf%offset(i)
369 ndst = this%send_buf%ndofs(i)
370 do concurrent(j = 1:ndst)
371 send_data(base + j) = u(sp(j))
372 end do
373
374 nbytes = int(ndst, c_size_t) * c_sizeof(rp_dummy)
375 ! Put data + signal dst's data_signals[my_rank].
377 c_loc(recv_data(this%send_buf%remote_offset(i) + 1)), &
378 c_loc(send_data(base + 1)), &
379 nbytes, &
380 c_loc(data_signals(pe_rank + 1)), &
381 this%iter, shmem_signal_set, dst)
382 end do
383 !$omp end do
384 else
385 ! Funnelled: every SHMEM call is left to the master and only the pack
386 ! is work-shared, one peer at a time. The per-peer wait/pack/put order
387 ! of the serial code is kept -- the ack is what makes a slab safe to
388 ! repack as well, since it means the previous round's non-blocking put
389 ! has been consumed and no longer reads our send buffer. The barrier
390 ! after the ack wait releases the team into the pack, and the implicit
391 ! barrier at end do completes the slab before the master puts it.
392 do i = 1, size(this%send_pe)
393 dst = this%send_pe(i)
394 base = this%send_buf%offset(i)
395 ndst = this%send_buf%ndofs(i)
396
397 !$omp master
398 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
399 shmem_cmp_ge, this%iter - 1_8)
400 !$omp end master
401 !$omp barrier
402
403 sp => this%send_dof(dst)%array()
404 !OCL NORECURRENCE, NOVREC, NOALIAS
405 !DIR$ CONCURRENT
406 !DIR$ IVDEP
407 !GCC$ ivdep
408 !NEC$ IVDEP
409 !$omp do
410 do j = 1, ndst
411 send_data(base + j) = u(sp(j))
412 end do
413 !$omp end do
414
415 !$omp master
416 nbytes = int(ndst, c_size_t) * c_sizeof(rp_dummy)
418 c_loc(recv_data(this%send_buf%remote_offset(i) + 1)), &
419 c_loc(send_data(base + 1)), &
420 nbytes, &
421 c_loc(data_signals(pe_rank + 1)), &
422 this%iter, shmem_signal_set, dst)
423 !$omp end master
424 end do
425 !$omp barrier
426 end if
427#endif
428 end subroutine gs_shmem_nbsend
429
431 subroutine gs_shmem_nbrecv(this, tag)
432 class(gs_shmem_t), intent(inout) :: this
433 integer, intent(in) :: tag
434
435 end subroutine gs_shmem_nbrecv
436
440 subroutine gs_shmem_nbwait(this, u, n, op, strm)
441 class(gs_shmem_t), intent(inout) :: this
442 integer, intent(in) :: n
443 real(kind=rp), dimension(n), intent(inout) :: u
444 type(c_ptr), intent(inout) :: strm
445 integer :: op
446 integer :: i, j, src, base, nsrc
447 integer(c_int64_t) :: dummy
448 integer , pointer :: sp(:)
449 real(kind=rp), pointer :: recv_data(:)
450 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
451#ifdef HAVE_OPENSHMEM
452
453 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
454 [max(this%recv_buf%max_total, 1)])
455 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
456 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
457
458 ! The loop over peers stays serial even when the library is thread
459 ! multiple: a dof shared by 3+ ranks appears in several recv_dof lists,
460 ! so reducing two slabs concurrently would race on that dof. The master
461 ! awaits and acks each slab in turn and the team reduces it, taking the
462 ! parallelism within the slab instead.
463 do i = 1, size(this%recv_pe)
464 src = this%recv_pe(i)
465 base = this%recv_buf%offset(i)
466 nsrc = this%recv_buf%ndofs(i)
467
468 ! Wait for data from src; data_signals[src] is set by src's put.
469 !$omp master
470 dummy = shmem_signal_wait_until(c_loc(data_signals(src + 1)), &
471 shmem_cmp_ge, this%iter)
472 !$omp end master
473 !$omp barrier
474
475 sp => this%recv_dof(src)%array()
476 select case (op)
477 case (gs_op_add)
478 !OCL NORECURRENCE, NOVREC, NOALIAS
479 !DIR$ CONCURRENT
480 !DIR$ IVDEP
481 !GCC$ ivdep
482 !NEC$ IVDEP
483 !$omp do
484 do j = 1, nsrc
485 u(sp(j)) = u(sp(j)) + recv_data(base + j)
486 end do
487 !$omp end do
488 case (gs_op_mul)
489 !OCL NORECURRENCE, NOVREC, NOALIAS
490 !DIR$ CONCURRENT
491 !DIR$ IVDEP
492 !GCC$ ivdep
493 !NEC$ IVDEP
494 !$omp do
495 do j = 1, nsrc
496 u(sp(j)) = u(sp(j)) * recv_data(base + j)
497 end do
498 !$omp end do
499 case (gs_op_min)
500 !OCL NORECURRENCE, NOVREC, NOALIAS
501 !DIR$ CONCURRENT
502 !DIR$ IVDEP
503 !GCC$ ivdep
504 !NEC$ IVDEP
505 !$omp do
506 do j = 1, nsrc
507 u(sp(j)) = min(u(sp(j)), recv_data(base + j))
508 end do
509 !$omp end do
510 case (gs_op_max)
511 !OCL NORECURRENCE, NOVREC, NOALIAS
512 !DIR$ CONCURRENT
513 !DIR$ IVDEP
514 !GCC$ ivdep
515 !NEC$ IVDEP
516 !$omp do
517 do j = 1, nsrc
518 u(sp(j)) = max(u(sp(j)), recv_data(base + j))
519 end do
520 !$omp end do
521 case default
522 call neko_error("Unknown operation in gs_nbwait_shmem")
523 end select
524
525 ! Tell src we are done with this round's buffer so they may
526 ! overwrite it on the next round. We set our own rank's slot
527 ! in src's ack_signals; src waits there on the next nbsend. The
528 ! implicit barrier at the end do above guarantees the whole team is
529 ! finished with the slab before it is released.
530 !$omp master
532 c_loc(ack_signals(pe_rank + 1)), &
533 this%iter, src)
534 !$omp end master
535 end do
536#endif
537 end subroutine gs_shmem_nbwait
538
543 subroutine gs_shmem_nbsend_vec(this, u, n, nc, tag, deps, strm)
544 class(gs_shmem_t), intent(inout) :: this
545 integer, intent(in) :: n, nc
546 real(kind=rp), dimension(nc*n), intent(inout) :: u
547 integer, intent(in) :: tag
548 type(c_ptr), intent(inout) :: deps
549 type(c_ptr), intent(inout) :: strm
550 integer :: i, j, c, dst, base, ndst
551 integer(c_size_t) :: nbytes
552 integer, pointer :: sp(:)
553 real(kind=rp), pointer :: send_data(:), recv_data(:)
554 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
555 real(c_rp) :: rp_dummy
556#ifdef HAVE_OPENSHMEM
557
558 !$omp master
559 this%iter = this%iter + 1
560 !$omp end master
561
562 call c_f_pointer(this%send_buf%buf_ptr, send_data, &
563 [max(nc*this%send_buf%max_total, 1)])
564 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
565 [max(nc*this%recv_buf%max_total, 1)])
566 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
567 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
568
569 !$omp barrier
570
571 ! Same threading split as the scalar path; see gs_shmem_nbsend.
573 !$omp do
574 do i = 1, size(this%send_pe)
575 dst = this%send_pe(i)
576
577 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
578 shmem_cmp_ge, this%iter - 1_8)
579
580 sp => this%send_dof(dst)%array()
581 base = this%send_buf%offset(i)
582 ndst = this%send_buf%ndofs(i)
583 do c = 1, nc
584 do concurrent(j = 1:ndst)
585 send_data(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
586 end do
587 end do
588
589 nbytes = int(nc*ndst, c_size_t) * c_sizeof(rp_dummy)
591 c_loc(recv_data(nc*this%send_buf%remote_offset(i) + 1)), &
592 c_loc(send_data(nc*base + 1)), &
593 nbytes, &
594 c_loc(data_signals(pe_rank + 1)), &
595 this%iter, shmem_signal_set, dst)
596 end do
597 !$omp end do
598 else
599 do i = 1, size(this%send_pe)
600 dst = this%send_pe(i)
601 base = this%send_buf%offset(i)
602 ndst = this%send_buf%ndofs(i)
603
604 !$omp master
605 call shmem_uint64_wait_until(c_loc(ack_signals(dst + 1)), &
606 shmem_cmp_ge, this%iter - 1_8)
607 !$omp end master
608 !$omp barrier
609
610 sp => this%send_dof(dst)%array()
611 !$omp do
612 do j = 1, ndst
613 do c = 1, nc
614 send_data(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
615 end do
616 end do
617 !$omp end do
618
619 !$omp master
620 nbytes = int(nc*ndst, c_size_t) * c_sizeof(rp_dummy)
622 c_loc(recv_data(nc*this%send_buf%remote_offset(i) + 1)), &
623 c_loc(send_data(nc*base + 1)), &
624 nbytes, &
625 c_loc(data_signals(pe_rank + 1)), &
626 this%iter, shmem_signal_set, dst)
627 !$omp end master
628 end do
629 !$omp barrier
630 end if
631#endif
632 end subroutine gs_shmem_nbsend_vec
633
635 subroutine gs_shmem_nbrecv_vec(this, tag, nc)
636 class(gs_shmem_t), intent(inout) :: this
637 integer, intent(in) :: tag, nc
638 end subroutine gs_shmem_nbrecv_vec
639
642 subroutine gs_shmem_nbwait_vec(this, u, n, nc, op, strm)
643 class(gs_shmem_t), intent(inout) :: this
644 integer, intent(in) :: n, nc
645 real(kind=rp), dimension(nc*n), intent(inout) :: u
646 type(c_ptr), intent(inout) :: strm
647 integer :: op
648 integer :: i, j, c, src, base, nsrc
649 integer(c_int64_t) :: dummy
650 integer, pointer :: sp(:)
651 real(kind=rp), pointer :: recv_data(:)
652 integer(c_int64_t), pointer :: data_signals(:), ack_signals(:)
653#ifdef HAVE_OPENSHMEM
654
655 call c_f_pointer(this%recv_buf%buf_ptr, recv_data, &
656 [max(nc*this%recv_buf%max_total, 1)])
657 call c_f_pointer(this%data_signals_ptr, data_signals, [pe_size])
658 call c_f_pointer(this%ack_signals_ptr, ack_signals, [pe_size])
659
660 ! Serial over peers (a dof shared by 3+ ranks appears in several recv
661 ! lists); parallelism is taken within each slab. See gs_shmem_nbwait.
662 do i = 1, size(this%recv_pe)
663 src = this%recv_pe(i)
664 base = this%recv_buf%offset(i)
665 nsrc = this%recv_buf%ndofs(i)
666
667 !$omp master
668 dummy = shmem_signal_wait_until(c_loc(data_signals(src + 1)), &
669 shmem_cmp_ge, this%iter)
670 !$omp end master
671 !$omp barrier
672
673 sp => this%recv_dof(src)%array()
674 select case (op)
675 case (gs_op_add)
676 !$omp do
677 do j = 1, nsrc
678 do c = 1, nc
679 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
680 recv_data(nc*base + (c-1)*nsrc + j)
681 end do
682 end do
683 !$omp end do
684 case (gs_op_mul)
685 !$omp do
686 do j = 1, nsrc
687 do c = 1, nc
688 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
689 recv_data(nc*base + (c-1)*nsrc + j)
690 end do
691 end do
692 !$omp end do
693 case (gs_op_min)
694 !$omp do
695 do j = 1, nsrc
696 do c = 1, nc
697 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
698 recv_data(nc*base + (c-1)*nsrc + j))
699 end do
700 end do
701 !$omp end do
702 case (gs_op_max)
703 !$omp do
704 do j = 1, nsrc
705 do c = 1, nc
706 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
707 recv_data(nc*base + (c-1)*nsrc + j))
708 end do
709 end do
710 !$omp end do
711 case default
712 call neko_error("Unknown operation in gs_shmem_nbwait_vec")
713 end select
714
715 !$omp master
717 c_loc(ack_signals(pe_rank + 1)), &
718 this%iter, src)
719 !$omp end master
720 end do
721#endif
722 end subroutine gs_shmem_nbwait_vec
723
724end 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: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
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:636
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:216
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:643
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:319
subroutine gs_shmem_buf_free(this)
Release symmetric memory and bookkeeping.
Definition gs_shmem.F90:195
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:544
subroutine gs_shmem_free(this)
Deallocate OpenSHMEM based communication method.
Definition gs_shmem.F90:287
subroutine gs_shmem_nbrecv(this, tag)
No-op: receives are completed via remote put-with-signal.
Definition gs_shmem.F90:432
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:441
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:13
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
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:52
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