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