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 ! The vector slabs are part of the symmetric/registered allocation
251 ! made above, which every rank has to take part in, so they cannot
252 ! be deferred to the first fused exchange: a rank with no shared
253 ! dofs never reaches it. See gs_comm_t%vec_ready.
254 this%vec_ready = .true.
255
256 ! The zeroing above is a local store into window memory; no rank may
257 ! issue RMA until every rank has done it.
258 call mpi_barrier(neko_comm, ierr)
259
260 end subroutine gs_mpi_rma_init
261
263 subroutine gs_mpi_rma_free(this)
264 class(gs_mpi_rma_t), intent(inout) :: this
265 integer :: ierr
266
267 if (this%win_alloc) then
268 ! MPI_Win_free is collective and completes the epoch; barrier first
269 ! so no rank is still spinning on, or putting into, a window that
270 ! another rank is about to tear down.
271 call mpi_barrier(neko_comm, ierr)
272 call mpi_win_unlock_all(this%win_sig, ierr)
273 call mpi_win_unlock_all(this%win_data, ierr)
274 call mpi_win_free(this%win_sig, ierr)
275 call mpi_win_free(this%win_data, ierr)
276 this%win_alloc = .false.
277 end if
278 this%recv_ptr = c_null_ptr
279 this%sig_ptr = c_null_ptr
280 this%recv_total = 0
281 this%iter = 0
282
283 if (allocated(this%send_buf)) deallocate(this%send_buf)
284 if (allocated(this%send_ndofs)) deallocate(this%send_ndofs)
285 if (allocated(this%send_offset)) deallocate(this%send_offset)
286 if (allocated(this%send_rdisp)) deallocate(this%send_rdisp)
287 if (allocated(this%recv_ndofs)) deallocate(this%recv_ndofs)
288 if (allocated(this%recv_offset)) deallocate(this%recv_offset)
289
290 call this%free_order()
291 call this%free_dofs()
292
293 end subroutine gs_mpi_rma_free
294
301 function gs_mpi_rma_load(s, slot) result(v)
302 ! The whole array is the dummy rather than the one element that is read:
303 ! a VOLATILE dummy fed from a pointer array has to be assumed-shape or a
304 ! pointer array itself. No INTENT on it either, since gfortran rejects
305 ! INTENT(IN) together with VOLATILE. Read only.
306 integer(kind=i8), volatile :: s(:)
307 integer, intent(in) :: slot
308 integer(kind=i8) :: v
309
310 v = s(slot)
311
312 end function gs_mpi_rma_load
313
320 subroutine gs_mpi_rma_wait_ge(this, slot, val)
321 class(gs_mpi_rma_t), intent(inout) :: this
322 integer, intent(in) :: slot
323 integer(kind=i8), intent(in) :: val
324 integer(kind=i8), pointer :: sig(:)
325 type(mpi_status) :: status
326 integer :: spin, ierr
327 logical :: flag
328
329 call c_f_pointer(this%sig_ptr, sig, [2*pe_size])
330
331 spin = 0
332 do
333 ! In the separate memory model the public window copy is only made
334 ! visible to local loads by a sync; in the unified model this is a
335 ! cheap memory barrier.
336 if (.not. this%unified) call mpi_win_sync(this%win_sig, ierr)
337
338 if (gs_mpi_rma_load(sig, slot) .ge. val) exit
339
340 ! Poke MPI so implementations without asynchronous progress still
341 ! advance incoming RMA. Not needed on hardware-driven one-sided
342 ! components, hence only every GS_MPI_RMA_POKE iterations.
343 spin = spin + 1
344 if (mod(spin, gs_mpi_rma_poke) .eq. 0) then
345 call mpi_iprobe(mpi_any_source, mpi_any_tag, neko_comm, flag, &
346 status, ierr)
347 end if
348 end do
349
350 end subroutine gs_mpi_rma_wait_ge
351
355 subroutine gs_mpi_rma_nbsend(this, u, n, tag, deps, strm)
356 class(gs_mpi_rma_t), intent(inout) :: this
357 integer, intent(in) :: n
358 real(kind=rp), dimension(n), intent(inout) :: u
359 integer, intent(in) :: tag
360 type(c_ptr), intent(inout) :: deps
361 type(c_ptr), intent(inout) :: strm
362 integer(kind=MPI_ADDRESS_KIND) :: rdisp
363 integer, pointer :: sp(:)
364 integer :: i, j, dst, base, ndst, ierr
365
366 ! Entered from inside the gs_op_vector OpenMP parallel region. Every MPI
367 ! call is funnelled through the master: RMA under MPI_THREAD_MULTIPLE is
368 ! the least tuned path in most implementations.
369 !$omp master
370 this%iter = this%iter + 1
371 !$omp end master
372
373 ! Pack and put one peer at a time.
374 do i = 1, size(this%send_pe)
375 dst = this%send_pe(i)
376 base = this%send_offset(i)
377 ndst = this%send_ndofs(i)
378 sp => this%send_dof(dst)%array()
379 !OCL NORECURRENCE, NOVREC, NOALIAS
380 !DIR$ CONCURRENT
381 !DIR$ IVDEP
382 !GCC$ ivdep
383 !NEC$ IVDEP
384 !$omp do
385 do j = 1, ndst
386 this%send_buf(base + j) = u(sp(j))
387 end do
388 !$omp end do
389
390 ! Wait for dst to have consumed our previous round before we
391 ! overwrite its receive window. The ack slots start at 0 and dst
392 ! writes iter after consuming, so round 1 passes immediately.
393 !$omp master
394 call gs_mpi_rma_wait_ge(this, pe_size + dst + 1, this%iter - 1_i8)
395
396 rdisp = int(this%send_rdisp(i), mpi_address_kind)
397 call mpi_put(this%send_buf(base + 1), ndst, mpi_real_precision, &
398 dst, rdisp, ndst, mpi_real_precision, this%win_data, ierr)
399 !$omp end master
400 end do
401
402 ! Announce each peer as soon as its own put is remotely complete. MPI
403 ! orders accumulates only against the same location, so the flush is
404 ! what stands in for a put-with-notify.
405 !$omp master
406 if (gs_mpi_rma_flush_all) call mpi_win_flush_all(this%win_data, ierr)
407 rdisp = int(pe_rank, mpi_address_kind)
408 do i = 1, size(this%send_pe)
409 dst = this%send_pe(i)
410 if (.not. gs_mpi_rma_flush_all) then
411 call mpi_win_flush(dst, this%win_data, ierr)
412 end if
413 call mpi_accumulate(this%iter, 1, mpi_integer8, dst, rdisp, 1, &
414 mpi_integer8, mpi_replace, this%win_sig, ierr)
415 end do
416 call mpi_win_flush_all(this%win_sig, ierr)
417 !$omp end master
418 !$omp barrier
419
420 end subroutine gs_mpi_rma_nbsend
421
423 subroutine gs_mpi_rma_nbrecv(this, tag)
424 class(gs_mpi_rma_t), intent(inout) :: this
425 integer, intent(in) :: tag
426
427 end subroutine gs_mpi_rma_nbrecv
428
431 subroutine gs_mpi_rma_nbwait(this, u, n, op, strm)
432 class(gs_mpi_rma_t), intent(inout) :: this
433 integer, intent(in) :: n
434 real(kind=rp), dimension(n), intent(inout) :: u
435 type(c_ptr), intent(inout) :: strm
436 integer :: op
437 integer(kind=MPI_ADDRESS_KIND) :: rdisp
438 integer, pointer :: sp(:)
439 real(kind=rp), pointer :: recv_data(:)
440 integer :: i, j, src, base, nsrc, ierr
441
442 call c_f_pointer(this%recv_ptr, recv_data, [max(this%recv_total, 1)])
443
444 ! Serial over peers: a dof shared by three or more ranks appears in
445 ! several recv_dof lists, so reducing two slabs concurrently would race
446 ! on it. The parallelism is taken within each slab instead.
447 do i = 1, size(this%recv_pe)
448 src = this%recv_pe(i)
449 base = this%recv_offset(i)
450 nsrc = this%recv_ndofs(i)
451
452 !$omp master
453 call gs_mpi_rma_wait_ge(this, src + 1, this%iter)
454 !$omp end master
455 !$omp barrier
456
457 sp => this%recv_dof(src)%array()
458 select case (op)
459 case (gs_op_add)
460 !OCL NORECURRENCE, NOVREC, NOALIAS
461 !DIR$ CONCURRENT
462 !DIR$ IVDEP
463 !GCC$ ivdep
464 !NEC$ IVDEP
465 !$omp do
466 do j = 1, nsrc
467 u(sp(j)) = u(sp(j)) + recv_data(base + j)
468 end do
469 !$omp end do
470 case (gs_op_mul)
471 !OCL NORECURRENCE, NOVREC, NOALIAS
472 !DIR$ CONCURRENT
473 !DIR$ IVDEP
474 !GCC$ ivdep
475 !NEC$ IVDEP
476 !$omp do
477 do j = 1, nsrc
478 u(sp(j)) = u(sp(j)) * recv_data(base + j)
479 end do
480 !$omp end do
481 case (gs_op_min)
482 !OCL NORECURRENCE, NOVREC, NOALIAS
483 !DIR$ CONCURRENT
484 !DIR$ IVDEP
485 !GCC$ ivdep
486 !NEC$ IVDEP
487 !$omp do
488 do j = 1, nsrc
489 u(sp(j)) = min(u(sp(j)), recv_data(base + j))
490 end do
491 !$omp end do
492 case (gs_op_max)
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)) = max(u(sp(j)), recv_data(base + j))
501 end do
502 !$omp end do
503 case default
504 call neko_error("Unknown operation in gs_mpi_rma_nbwait")
505 end select
506
507 ! The implicit barrier at end do guarantees the whole team is done
508 ! with the slab before it is released back to src.
509 !$omp master
510 rdisp = int(pe_size + pe_rank, mpi_address_kind)
511 call mpi_accumulate(this%iter, 1, mpi_integer8, src, rdisp, 1, &
512 mpi_integer8, mpi_replace, this%win_sig, ierr)
513 !$omp end master
514 end do
515
516 ! An accumulate is only guaranteed to be issued at a flush, so the acks
517 ! have to be pushed out before we go back to computing.
518 !$omp master
519 call mpi_win_flush_all(this%win_sig, ierr)
520 !$omp end master
521 !$omp barrier
522
523 end subroutine gs_mpi_rma_nbwait
524
529 subroutine gs_mpi_rma_nbsend_vec(this, u, n, nc, tag, deps, strm)
530 class(gs_mpi_rma_t), intent(inout) :: this
531 integer, intent(in) :: n, nc
532 real(kind=rp), dimension(nc*n), intent(inout) :: u
533 integer, intent(in) :: tag
534 type(c_ptr), intent(inout) :: deps
535 type(c_ptr), intent(inout) :: strm
536 integer(kind=MPI_ADDRESS_KIND) :: rdisp
537 integer, pointer :: sp(:)
538 integer :: i, j, c, dst, base, ndst, ierr
539
540 !$omp master
541 this%iter = this%iter + 1
542 !$omp end master
543
544 ! Pack and put per peer, as in the scalar path; see gs_mpi_rma_nbsend.
545 do i = 1, size(this%send_pe)
546 dst = this%send_pe(i)
547 base = this%send_offset(i)
548 ndst = this%send_ndofs(i)
549 sp => this%send_dof(dst)%array()
550 !$omp do
551 do j = 1, ndst
552 do c = 1, nc
553 this%send_buf(nc*base + (c-1)*ndst + j) = u((c-1)*n + sp(j))
554 end do
555 end do
556 !$omp end do
557
558 !$omp master
559 call gs_mpi_rma_wait_ge(this, pe_size + dst + 1, this%iter - 1_i8)
560
561 rdisp = int(nc*this%send_rdisp(i), mpi_address_kind)
562 call mpi_put(this%send_buf(nc*base + 1), nc*ndst, &
563 mpi_real_precision, dst, rdisp, nc*ndst, mpi_real_precision, &
564 this%win_data, ierr)
565 !$omp end master
566 end do
567
568 !$omp master
569 if (gs_mpi_rma_flush_all) call mpi_win_flush_all(this%win_data, ierr)
570 rdisp = int(pe_rank, mpi_address_kind)
571 do i = 1, size(this%send_pe)
572 dst = this%send_pe(i)
573 if (.not. gs_mpi_rma_flush_all) then
574 call mpi_win_flush(dst, this%win_data, ierr)
575 end if
576 call mpi_accumulate(this%iter, 1, mpi_integer8, dst, rdisp, 1, &
577 mpi_integer8, mpi_replace, this%win_sig, ierr)
578 end do
579 call mpi_win_flush_all(this%win_sig, ierr)
580 !$omp end master
581 !$omp barrier
582
583 end subroutine gs_mpi_rma_nbsend_vec
584
586 subroutine gs_mpi_rma_nbrecv_vec(this, tag, nc)
587 class(gs_mpi_rma_t), intent(inout) :: this
588 integer, intent(in) :: tag, nc
589
590 end subroutine gs_mpi_rma_nbrecv_vec
591
594 subroutine gs_mpi_rma_nbwait_vec(this, u, n, nc, op, strm)
595 class(gs_mpi_rma_t), intent(inout) :: this
596 integer, intent(in) :: n, nc
597 real(kind=rp), dimension(nc*n), intent(inout) :: u
598 type(c_ptr), intent(inout) :: strm
599 integer :: op
600 integer(kind=MPI_ADDRESS_KIND) :: rdisp
601 integer, pointer :: sp(:)
602 real(kind=rp), pointer :: recv_data(:)
603 integer :: i, j, c, src, base, nsrc, ierr
604
605 call c_f_pointer(this%recv_ptr, recv_data, [max(nc*this%recv_total, 1)])
606
607 do i = 1, size(this%recv_pe)
608 src = this%recv_pe(i)
609 base = this%recv_offset(i)
610 nsrc = this%recv_ndofs(i)
611
612 !$omp master
613 call gs_mpi_rma_wait_ge(this, src + 1, this%iter)
614 !$omp end master
615 !$omp barrier
616
617 sp => this%recv_dof(src)%array()
618 select case (op)
619 case (gs_op_add)
620 !$omp do
621 do j = 1, nsrc
622 do c = 1, nc
623 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
624 recv_data(nc*base + (c-1)*nsrc + j)
625 end do
626 end do
627 !$omp end do
628 case (gs_op_mul)
629 !$omp do
630 do j = 1, nsrc
631 do c = 1, nc
632 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
633 recv_data(nc*base + (c-1)*nsrc + j)
634 end do
635 end do
636 !$omp end do
637 case (gs_op_min)
638 !$omp do
639 do j = 1, nsrc
640 do c = 1, nc
641 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
642 recv_data(nc*base + (c-1)*nsrc + j))
643 end do
644 end do
645 !$omp end do
646 case (gs_op_max)
647 !$omp do
648 do j = 1, nsrc
649 do c = 1, nc
650 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
651 recv_data(nc*base + (c-1)*nsrc + j))
652 end do
653 end do
654 !$omp end do
655 case default
656 call neko_error("Unknown operation in gs_mpi_rma_nbwait_vec")
657 end select
658
659 !$omp master
660 rdisp = int(pe_size + pe_rank, mpi_address_kind)
661 call mpi_accumulate(this%iter, 1, mpi_integer8, src, rdisp, 1, &
662 mpi_integer8, mpi_replace, this%win_sig, ierr)
663 !$omp end master
664 end do
665
666 !$omp master
667 call mpi_win_flush_all(this%win_sig, ierr)
668 !$omp end master
669 !$omp barrier
670
671 end subroutine gs_mpi_rma_nbwait_vec
672
673end 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