Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_mpi_rma.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, i8
36 use gs_comm, only : gs_comm_t, gs_vec_nc
38 use stack, only : stack_i4_t
40 use utils, only : neko_error
41 use mpi_f08, only : mpi_win, mpi_info, mpi_win_allocate, mpi_win_free, &
42 mpi_win_lock_all, mpi_win_unlock_all, mpi_win_flush, &
43 mpi_win_flush_all, &
44 mpi_win_sync, mpi_win_get_attr, mpi_put, mpi_accumulate, &
45 mpi_alltoall, mpi_barrier, mpi_iprobe, mpi_status, &
46 mpi_info_create, mpi_info_set, mpi_info_free, &
47 mpi_integer, mpi_integer8, mpi_replace, mpi_mode_nocheck, &
48 mpi_address_kind, mpi_win_model, mpi_win_unified, &
49 mpi_any_source, mpi_any_tag
50 use, intrinsic :: iso_c_binding, only : c_ptr, c_null_ptr, c_f_pointer, &
51 c_associated, c_sizeof
52 implicit none
53 private
54
58 logical, parameter, public :: gs_mpi_rma_avail = .true.
59
61 integer, parameter :: gs_mpi_rma_poke = 64
62
66 logical, save :: gs_mpi_rma_flush_all = .true.
69 logical, save :: gs_mpi_rma_flush_bound = .false.
70
73 type, public, extends(gs_comm_t) :: gs_mpi_rma_t
75 real(kind=rp), allocatable :: send_buf(:)
77 integer, allocatable :: send_ndofs(:)
79 integer, allocatable :: send_offset(:)
82 integer, allocatable :: send_rdisp(:)
83
85 type(mpi_win) :: win_data
87 type(c_ptr) :: recv_ptr = c_null_ptr
89 integer, allocatable :: recv_ndofs(:)
91 integer, allocatable :: recv_offset(:)
93 integer :: recv_total = 0
94
96 type(mpi_win) :: win_sig
98 type(c_ptr) :: sig_ptr = c_null_ptr
99
103 integer(kind=i8) :: iter = 0
104
108 logical :: unified = .true.
109
112 logical :: win_alloc = .false.
113 contains
114 procedure, pass(this) :: init => gs_mpi_rma_init
115 procedure, pass(this) :: free => gs_mpi_rma_free
116 procedure, pass(this) :: nbsend => gs_mpi_rma_nbsend
117 procedure, pass(this) :: nbrecv => gs_mpi_rma_nbrecv
118 procedure, pass(this) :: nbwait => gs_mpi_rma_nbwait
119 procedure, pass(this) :: nbsend_vec => gs_mpi_rma_nbsend_vec
120 procedure, pass(this) :: nbrecv_vec => gs_mpi_rma_nbrecv_vec
121 procedure, pass(this) :: nbwait_vec => gs_mpi_rma_nbwait_vec
122 end type gs_mpi_rma_t
123
124contains
125
127 subroutine gs_mpi_rma_init(this, send_pe, recv_pe)
128 class(gs_mpi_rma_t), intent(inout) :: this
129 type(stack_i4_t), intent(inout) :: send_pe
130 type(stack_i4_t), intent(inout) :: recv_pe
131 type(mpi_info) :: info
132 integer(kind=MPI_ADDRESS_KIND) :: win_size, attr_val
133 integer, allocatable :: local_disp(:), remote_disp(:)
134 integer(kind=i8), pointer :: sig(:)
135 integer :: i, n, send_total, ierr, disp_unit, env_len
136 integer(kind=i8) :: i8_dummy
137 real(kind=rp) :: rp_dummy
138 character(len=255) :: env_val
139 logical :: flag
140
141 ! No self-free here: gs_schedule has just filled send_dof/recv_dof and
142 ! gs_mpi_rma_free would deallocate them again. Like every other comm
143 ! backend, init assumes a freshly allocated object.
144 call this%init_order(send_pe, recv_pe)
145
146 ! Bind the flush strategy once; every instance shares it, so that an
147 ! A/B run measures one protocol rather than a mixture.
148 if (.not. gs_mpi_rma_flush_bound) then
149 call get_environment_variable("NEKO_GS_RMA_FLUSH_ALL", env_val, env_len)
150 if (env_len .gt. 0) then
151 env_len = min(env_len, len(env_val))
152 gs_mpi_rma_flush_all = (env_val(1:env_len) .ne. '0')
153 end if
155 end if
156
157 ! Send side: exact local sizing, plain memory
158 n = size(this%send_pe)
159 allocate(this%send_ndofs(n), this%send_offset(n), this%send_rdisp(n))
160 send_total = 0
161 do i = 1, n
162 this%send_ndofs(i) = this%send_dof(this%send_pe(i))%size()
163 this%send_offset(i) = send_total
164 send_total = send_total + this%send_ndofs(i)
165 end do
166 this%send_rdisp = -1
167 allocate(this%send_buf(max(gs_vec_nc*send_total, 1)))
168
169 ! Receive side: exact local sizing, exposed in a window
170 n = size(this%recv_pe)
171 allocate(this%recv_ndofs(n), this%recv_offset(n))
172 this%recv_total = 0
173 do i = 1, n
174 this%recv_ndofs(i) = this%recv_dof(this%recv_pe(i))%size()
175 this%recv_offset(i) = this%recv_total
176 this%recv_total = this%recv_total + this%recv_ndofs(i)
177 end do
178
179 ! same_disp_unit lets the implementation skip the per-target lookup on
180 ! every operation.
181 !
182 ! The two accumulate assertions matter more than they look. By default
183 ! MPI must preserve rar/raw/war/waw ordering between accumulates, which
184 ! can rule out a hardware-atomic path for the signals in favour of a
185 ! general, ordered one -- and the signals are two of the four RMA calls
186 ! this backend makes per peer per round. Neither assertion costs
187 ! anything here: a round issues at most one accumulate to any given
188 ! location and consecutive rounds are separated by a flush, so no two
189 ! are ever outstanding against the same slot, and every accumulate in
190 ! the protocol is MPI_REPLACE.
191 call mpi_info_create(info, ierr)
192 call mpi_info_set(info, 'same_disp_unit', 'true', ierr)
193 call mpi_info_set(info, 'accumulate_ordering', 'none', ierr)
194 call mpi_info_set(info, 'accumulate_ops', 'same_op', ierr)
195
196 disp_unit = int(c_sizeof(rp_dummy))
197 win_size = int(max(gs_vec_nc*this%recv_total, 1), mpi_address_kind) * &
198 int(disp_unit, mpi_address_kind)
199 call mpi_win_allocate(win_size, disp_unit, info, neko_comm, &
200 this%recv_ptr, this%win_data, ierr)
201 if (.not. c_associated(this%recv_ptr)) then
202 call neko_error('MPI_Win_allocate failed for gs_mpi_rma data window')
203 end if
204
205 disp_unit = int(c_sizeof(i8_dummy))
206 win_size = int(2*pe_size, mpi_address_kind) * &
207 int(disp_unit, mpi_address_kind)
208 call mpi_win_allocate(win_size, disp_unit, info, neko_comm, &
209 this%sig_ptr, this%win_sig, ierr)
210 if (.not. c_associated(this%sig_ptr)) then
211 call neko_error('MPI_Win_allocate failed for gs_mpi_rma signal window')
212 end if
213
214 call mpi_info_free(info, ierr)
215 this%win_alloc = .true.
216
217 ! MPI_Win_allocate does not zero the memory, and the protocol reads the
218 ! counters before anyone has written them (iter == 1 waits for an ack
219 ! of >= 0).
220 call c_f_pointer(this%sig_ptr, sig, [2*pe_size])
221 sig = 0_i8
222
223 ! Both windows come from the same call sequence, so one query answers
224 ! for both.
225 call mpi_win_get_attr(this%win_sig, mpi_win_model, attr_val, flag, ierr)
226 this%unified = flag .and. (int(attr_val) .eq. mpi_win_unified)
227
228 call mpi_win_lock_all(mpi_mode_nocheck, this%win_data, ierr)
229 call mpi_win_lock_all(mpi_mode_nocheck, this%win_sig, ierr)
230
231 ! Tell each peer where in our receive window its slab belongs. Alltoall
232 ! rather than pairwise Isend/Irecv for the same reason as in gs_shmem:
233 ! send_pe and recv_pe follow a shifted-modulo pattern that deadlocked
234 ! pairwise exchanges at certain rank counts, and the payload is one int
235 ! per rank.
236 allocate(local_disp(0:pe_size - 1), remote_disp(0:pe_size - 1))
237 local_disp = -1
238 do i = 1, size(this%recv_pe)
239 local_disp(this%recv_pe(i)) = this%recv_offset(i)
240 end do
241 call mpi_alltoall(local_disp, 1, mpi_integer, &
242 remote_disp, 1, mpi_integer, neko_comm, ierr)
243 do i = 1, size(this%send_pe)
244 this%send_rdisp(i) = remote_disp(this%send_pe(i))
245 end do
246 deallocate(local_disp, remote_disp)
247
248 this%iter = 0
249 this%vec_supported = .true.
250
251 ! The zeroing above is a local store into window memory; no rank may
252 ! issue RMA until every rank has done it.
253 call mpi_barrier(neko_comm, ierr)
254
255 end subroutine gs_mpi_rma_init
256
258 subroutine gs_mpi_rma_free(this)
259 class(gs_mpi_rma_t), intent(inout) :: this
260 integer :: ierr
261
262 if (this%win_alloc) then
263 ! MPI_Win_free is collective and completes the epoch; barrier first
264 ! so no rank is still spinning on, or putting into, a window that
265 ! another rank is about to tear down.
266 call mpi_barrier(neko_comm, ierr)
267 call mpi_win_unlock_all(this%win_sig, ierr)
268 call mpi_win_unlock_all(this%win_data, ierr)
269 call mpi_win_free(this%win_sig, ierr)
270 call mpi_win_free(this%win_data, ierr)
271 this%win_alloc = .false.
272 end if
273 this%recv_ptr = c_null_ptr
274 this%sig_ptr = c_null_ptr
275 this%recv_total = 0
276 this%iter = 0
277
278 if (allocated(this%send_buf)) deallocate(this%send_buf)
279 if (allocated(this%send_ndofs)) deallocate(this%send_ndofs)
280 if (allocated(this%send_offset)) deallocate(this%send_offset)
281 if (allocated(this%send_rdisp)) deallocate(this%send_rdisp)
282 if (allocated(this%recv_ndofs)) deallocate(this%recv_ndofs)
283 if (allocated(this%recv_offset)) deallocate(this%recv_offset)
284
285 call this%free_order()
286 call this%free_dofs()
287
288 end subroutine gs_mpi_rma_free
289
296 function gs_mpi_rma_load(s, slot) result(v)
297 ! The whole array is the dummy rather than the one element that is read:
298 ! a VOLATILE dummy fed from a pointer array has to be assumed-shape or a
299 ! pointer array itself. No INTENT on it either, since gfortran rejects
300 ! INTENT(IN) together with VOLATILE. Read only.
301 integer(kind=i8), volatile :: s(:)
302 integer, intent(in) :: slot
303 integer(kind=i8) :: v
304
305 v = s(slot)
306
307 end function gs_mpi_rma_load
308
315 subroutine gs_mpi_rma_wait_ge(this, slot, val)
316 class(gs_mpi_rma_t), intent(inout) :: this
317 integer, intent(in) :: slot
318 integer(kind=i8), intent(in) :: val
319 integer(kind=i8), pointer :: sig(:)
320 type(mpi_status) :: status
321 integer :: spin, ierr
322 logical :: flag
323
324 call c_f_pointer(this%sig_ptr, sig, [2*pe_size])
325
326 spin = 0
327 do
328 ! In the separate memory model the public window copy is only made
329 ! visible to local loads by a sync; in the unified model this is a
330 ! cheap memory barrier.
331 if (.not. this%unified) call mpi_win_sync(this%win_sig, ierr)
332
333 if (gs_mpi_rma_load(sig, slot) .ge. val) exit
334
335 ! Poke MPI so implementations without asynchronous progress still
336 ! advance incoming RMA. Not needed on hardware-driven one-sided
337 ! components, hence only every GS_MPI_RMA_POKE iterations.
338 spin = spin + 1
339 if (mod(spin, gs_mpi_rma_poke) .eq. 0) then
340 call mpi_iprobe(mpi_any_source, mpi_any_tag, neko_comm, flag, &
341 status, ierr)
342 end if
343 end do
344
345 end subroutine gs_mpi_rma_wait_ge
346
350 subroutine gs_mpi_rma_nbsend(this, u, n, tag, deps, strm)
351 class(gs_mpi_rma_t), intent(inout) :: this
352 integer, intent(in) :: n
353 real(kind=rp), dimension(n), intent(inout) :: u
354 integer, intent(in) :: tag
355 type(c_ptr), intent(inout) :: deps
356 type(c_ptr), intent(inout) :: strm
357 integer(kind=MPI_ADDRESS_KIND) :: rdisp
358 integer, pointer :: sp(:)
359 integer :: i, j, dst, base, ndst, ierr
360
361 ! Entered from inside the gs_op_vector OpenMP parallel region. Every MPI
362 ! call is funnelled through the master: RMA under MPI_THREAD_MULTIPLE is
363 ! the least tuned path in most implementations.
364 !$omp master
365 this%iter = this%iter + 1
366 !$omp end master
367
368 ! Pack and put one peer at a time.
369 do i = 1, size(this%send_pe)
370 dst = this%send_pe(i)
371 base = this%send_offset(i)
372 ndst = this%send_ndofs(i)
373 sp => this%send_dof(dst)%array()
374 !OCL NORECURRENCE, NOVREC, NOALIAS
375 !DIR$ CONCURRENT
376 !DIR$ IVDEP
377 !GCC$ ivdep
378 !NEC$ IVDEP
379 !$omp do
380 do j = 1, ndst
381 this%send_buf(base + j) = u(sp(j))
382 end do
383 !$omp end do
384
385 ! Wait for dst to have consumed our previous round before we
386 ! overwrite its receive window. The ack slots start at 0 and dst
387 ! writes iter after consuming, so round 1 passes immediately.
388 !$omp master
389 call gs_mpi_rma_wait_ge(this, pe_size + dst + 1, this%iter - 1_i8)
390
391 rdisp = int(this%send_rdisp(i), mpi_address_kind)
392 call mpi_put(this%send_buf(base + 1), ndst, mpi_real_precision, &
393 dst, rdisp, ndst, mpi_real_precision, this%win_data, ierr)
394 !$omp end master
395 end do
396
397 ! Announce each peer as soon as its own put is remotely complete. MPI
398 ! orders accumulates only against the same location, so the flush is
399 ! what stands in for a put-with-notify.
400 !$omp master
401 if (gs_mpi_rma_flush_all) call mpi_win_flush_all(this%win_data, ierr)
402 rdisp = int(pe_rank, mpi_address_kind)
403 do i = 1, size(this%send_pe)
404 dst = this%send_pe(i)
405 if (.not. gs_mpi_rma_flush_all) then
406 call mpi_win_flush(dst, this%win_data, ierr)
407 end if
408 call mpi_accumulate(this%iter, 1, mpi_integer8, dst, rdisp, 1, &
409 mpi_integer8, mpi_replace, this%win_sig, ierr)
410 end do
411 call mpi_win_flush_all(this%win_sig, ierr)
412 !$omp end master
413 !$omp barrier
414
415 end subroutine gs_mpi_rma_nbsend
416
418 subroutine gs_mpi_rma_nbrecv(this, tag)
419 class(gs_mpi_rma_t), intent(inout) :: this
420 integer, intent(in) :: tag
421
422 end subroutine gs_mpi_rma_nbrecv
423
426 subroutine gs_mpi_rma_nbwait(this, u, n, op, strm)
427 class(gs_mpi_rma_t), intent(inout) :: this
428 integer, intent(in) :: n
429 real(kind=rp), dimension(n), intent(inout) :: u
430 type(c_ptr), intent(inout) :: strm
431 integer :: op
432 integer(kind=MPI_ADDRESS_KIND) :: rdisp
433 integer, pointer :: sp(:)
434 real(kind=rp), pointer :: recv_data(:)
435 integer :: i, j, src, base, nsrc, ierr
436
437 call c_f_pointer(this%recv_ptr, recv_data, [max(this%recv_total, 1)])
438
439 ! Serial over peers: a dof shared by three or more ranks appears in
440 ! several recv_dof lists, so reducing two slabs concurrently would race
441 ! on it. The parallelism is taken within each slab instead.
442 do i = 1, size(this%recv_pe)
443 src = this%recv_pe(i)
444 base = this%recv_offset(i)
445 nsrc = this%recv_ndofs(i)
446
447 !$omp master
448 call gs_mpi_rma_wait_ge(this, src + 1, this%iter)
449 !$omp end master
450 !$omp barrier
451
452 sp => this%recv_dof(src)%array()
453 select case (op)
454 case (gs_op_add)
455 !OCL NORECURRENCE, NOVREC, NOALIAS
456 !DIR$ CONCURRENT
457 !DIR$ IVDEP
458 !GCC$ ivdep
459 !NEC$ IVDEP
460 !$omp do
461 do j = 1, nsrc
462 u(sp(j)) = u(sp(j)) + recv_data(base + j)
463 end do
464 !$omp end do
465 case (gs_op_mul)
466 !OCL NORECURRENCE, NOVREC, NOALIAS
467 !DIR$ CONCURRENT
468 !DIR$ IVDEP
469 !GCC$ ivdep
470 !NEC$ IVDEP
471 !$omp do
472 do j = 1, nsrc
473 u(sp(j)) = u(sp(j)) * recv_data(base + j)
474 end do
475 !$omp end do
476 case (gs_op_min)
477 !OCL NORECURRENCE, NOVREC, NOALIAS
478 !DIR$ CONCURRENT
479 !DIR$ IVDEP
480 !GCC$ ivdep
481 !NEC$ IVDEP
482 !$omp do
483 do j = 1, nsrc
484 u(sp(j)) = min(u(sp(j)), recv_data(base + j))
485 end do
486 !$omp end do
487 case (gs_op_max)
488 !OCL NORECURRENCE, NOVREC, NOALIAS
489 !DIR$ CONCURRENT
490 !DIR$ IVDEP
491 !GCC$ ivdep
492 !NEC$ IVDEP
493 !$omp do
494 do j = 1, nsrc
495 u(sp(j)) = max(u(sp(j)), recv_data(base + j))
496 end do
497 !$omp end do
498 case default
499 call neko_error("Unknown operation in gs_mpi_rma_nbwait")
500 end select
501
502 ! The implicit barrier at end do guarantees the whole team is done
503 ! with the slab before it is released back to src.
504 !$omp master
505 rdisp = int(pe_size + pe_rank, mpi_address_kind)
506 call mpi_accumulate(this%iter, 1, mpi_integer8, src, rdisp, 1, &
507 mpi_integer8, mpi_replace, this%win_sig, ierr)
508 !$omp end master
509 end do
510
511 ! An accumulate is only guaranteed to be issued at a flush, so the acks
512 ! have to be pushed out before we go back to computing.
513 !$omp master
514 call mpi_win_flush_all(this%win_sig, ierr)
515 !$omp end master
516 !$omp barrier
517
518 end subroutine gs_mpi_rma_nbwait
519
524 subroutine gs_mpi_rma_nbsend_vec(this, u, n, nc, tag, deps, strm)
525 class(gs_mpi_rma_t), intent(inout) :: this
526 integer, intent(in) :: n, nc
527 real(kind=rp), dimension(nc*n), intent(inout) :: u
528 integer, intent(in) :: tag
529 type(c_ptr), intent(inout) :: deps
530 type(c_ptr), intent(inout) :: strm
531 integer(kind=MPI_ADDRESS_KIND) :: rdisp
532 integer, pointer :: sp(:)
533 integer :: i, j, c, dst, base, ndst, ierr
534
535 !$omp master
536 this%iter = this%iter + 1
537 !$omp end master
538
539 ! Pack and put per peer, as in the scalar path; see gs_mpi_rma_nbsend.
540 do i = 1, size(this%send_pe)
541 dst = this%send_pe(i)
542 base = this%send_offset(i)
543 ndst = this%send_ndofs(i)
544 sp => this%send_dof(dst)%array()
545 !$omp do
546 do j = 1, ndst
547 do c = 1, nc
548 this%send_buf(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
549 end do
550 end do
551 !$omp end do
552
553 !$omp master
554 call gs_mpi_rma_wait_ge(this, pe_size + dst + 1, this%iter - 1_i8)
555
556 rdisp = int(nc*this%send_rdisp(i), mpi_address_kind)
557 call mpi_put(this%send_buf(nc*base + 1), nc*ndst, &
558 mpi_real_precision, dst, rdisp, nc*ndst, mpi_real_precision, &
559 this%win_data, ierr)
560 !$omp end master
561 end do
562
563 !$omp master
564 if (gs_mpi_rma_flush_all) call mpi_win_flush_all(this%win_data, ierr)
565 rdisp = int(pe_rank, mpi_address_kind)
566 do i = 1, size(this%send_pe)
567 dst = this%send_pe(i)
568 if (.not. gs_mpi_rma_flush_all) then
569 call mpi_win_flush(dst, this%win_data, ierr)
570 end if
571 call mpi_accumulate(this%iter, 1, mpi_integer8, dst, rdisp, 1, &
572 mpi_integer8, mpi_replace, this%win_sig, ierr)
573 end do
574 call mpi_win_flush_all(this%win_sig, ierr)
575 !$omp end master
576 !$omp barrier
577
578 end subroutine gs_mpi_rma_nbsend_vec
579
581 subroutine gs_mpi_rma_nbrecv_vec(this, tag, nc)
582 class(gs_mpi_rma_t), intent(inout) :: this
583 integer, intent(in) :: tag, nc
584
585 end subroutine gs_mpi_rma_nbrecv_vec
586
589 subroutine gs_mpi_rma_nbwait_vec(this, u, n, nc, op, strm)
590 class(gs_mpi_rma_t), intent(inout) :: this
591 integer, intent(in) :: n, nc
592 real(kind=rp), dimension(nc*n), intent(inout) :: u
593 type(c_ptr), intent(inout) :: strm
594 integer :: op
595 integer(kind=MPI_ADDRESS_KIND) :: rdisp
596 integer, pointer :: sp(:)
597 real(kind=rp), pointer :: recv_data(:)
598 integer :: i, j, c, src, base, nsrc, ierr
599
600 call c_f_pointer(this%recv_ptr, recv_data, [max(nc*this%recv_total, 1)])
601
602 do i = 1, size(this%recv_pe)
603 src = this%recv_pe(i)
604 base = this%recv_offset(i)
605 nsrc = this%recv_ndofs(i)
606
607 !$omp master
608 call gs_mpi_rma_wait_ge(this, src + 1, this%iter)
609 !$omp end master
610 !$omp barrier
611
612 sp => this%recv_dof(src)%array()
613 select case (op)
614 case (gs_op_add)
615 !$omp do
616 do j = 1, nsrc
617 do c = 1, nc
618 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
619 recv_data(nc*base + (c-1)*nsrc + j)
620 end do
621 end do
622 !$omp end do
623 case (gs_op_mul)
624 !$omp do
625 do j = 1, nsrc
626 do c = 1, nc
627 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
628 recv_data(nc*base + (c-1)*nsrc + j)
629 end do
630 end do
631 !$omp end do
632 case (gs_op_min)
633 !$omp do
634 do j = 1, nsrc
635 do c = 1, nc
636 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
637 recv_data(nc*base + (c-1)*nsrc + j))
638 end do
639 end do
640 !$omp end do
641 case (gs_op_max)
642 !$omp do
643 do j = 1, nsrc
644 do c = 1, nc
645 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
646 recv_data(nc*base + (c-1)*nsrc + j))
647 end do
648 end do
649 !$omp end do
650 case default
651 call neko_error("Unknown operation in gs_mpi_rma_nbwait_vec")
652 end select
653
654 !$omp master
655 rdisp = int(pe_size + pe_rank, mpi_address_kind)
656 call mpi_accumulate(this%iter, 1, mpi_integer8, src, rdisp, 1, &
657 mpi_integer8, mpi_replace, this%win_sig, ierr)
658 !$omp end master
659 end do
660
661 !$omp master
662 call mpi_win_flush_all(this%win_sig, ierr)
663 !$omp end master
664 !$omp barrier
665
666 end subroutine gs_mpi_rma_nbwait_vec
667
668end module gs_mpi_rma
Definition comm.F90:1
type(mpi_datatype), public mpi_real_precision
MPI type for working precision of REAL types.
Definition comm.F90:54
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 MPI one-sided (RMA) gather-scatter communication.
logical, parameter, public gs_mpi_rma_avail
MPI RMA needs nothing beyond MPI-3, so the backend is always built. Kept as a parameter for symmetry ...
subroutine gs_mpi_rma_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....
subroutine gs_mpi_rma_nbrecv_vec(this, tag, nc)
No-op: receives are completed by the remote put and its signal.
integer, parameter gs_mpi_rma_poke
Number of spin iterations between the progress pokes in gs_mpi_rma_wait_ge.
subroutine gs_mpi_rma_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...
subroutine gs_mpi_rma_free(this)
Deallocate MPI RMA based communication method.
integer(kind=i8) function gs_mpi_rma_load(s, slot)
Reload a signal counter through a VOLATILE dummy, so the spin in gs_mpi_rma_wait_ge reads memory on e...
subroutine gs_mpi_rma_nbwait(this, u, n, op, strm)
Wait per neighbour for the signal that its data has landed, reduce the slab into u,...
logical, save gs_mpi_rma_flush_bound
Whether the flush strategy has been read from the environment. It is a program-wide binding,...
subroutine gs_mpi_rma_nbsend(this, u, n, tag, deps, strm)
Pack the gathered shared dofs and put them into each neighbour's receive window, then announce the pu...
subroutine gs_mpi_rma_wait_ge(this, slot, val)
Spin until the local signal counter in slot slot has reached val. Called by the master thread only.
subroutine gs_mpi_rma_init(this, send_pe, recv_pe)
Initialise MPI RMA based communication method.
logical, save gs_mpi_rma_flush_all
Whether to complete the payload puts with a single MPI_Win_flush_all rather than one MPI_Win_flush pe...
subroutine gs_mpi_rma_nbrecv(this, tag)
No-op: receives are completed by the remote put and its signal.
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 i8
Definition num_types.f90:7
integer, parameter, public sp
Definition num_types.f90:8
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:53
Gather-scatter communication using MPI one-sided puts into a passive target window,...
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40