Neko 1.99.6
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_mpi.f90
Go to the documentation of this file.
1! Copyright (c) 2020-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!
34module gs_mpi
35 use num_types, only : rp
38 use stack, only : stack_i4_t
39 use mpi_f08, only : mpi_statuses_ignore, mpi_status, &
40 mpi_request, mpi_isend, mpi_irecv, mpi_testsome, mpi_testall, &
41 mpi_thread_multiple
43 use, intrinsic :: iso_c_binding
44 use utils, only : neko_error
45 implicit none
46 private
47
49 type, public, extends(gs_comm_t) :: gs_mpi_t
51 real(kind=rp), allocatable :: send_buf(:)
53 real(kind=rp), allocatable :: recv_buf(:)
55 integer, allocatable :: send_len(:), recv_len(:)
57 integer, allocatable :: send_offset(:), recv_offset(:)
59 type(mpi_request), allocatable :: send_request(:), recv_request(:)
62 integer, allocatable :: recv_indices(:)
63 type(mpi_status), allocatable :: recv_statuses(:)
64 integer :: ncompleted
68 real(kind=rp), allocatable :: send_buf_v(:)
69 real(kind=rp), allocatable :: recv_buf_v(:)
70 contains
71 procedure, pass(this) :: init => gs_mpi_init
72 procedure, pass(this) :: free => gs_mpi_free
74 procedure, pass(this) :: nbsend => gs_nbsend_mpi
75 procedure, pass(this) :: nbrecv => gs_nbrecv_mpi
76 procedure, pass(this) :: nbwait => gs_nbwait_mpi
77 procedure, pass(this) :: nbsend_vec => gs_nbsend_vec_mpi
78 procedure, pass(this) :: nbrecv_vec => gs_nbrecv_vec_mpi
79 procedure, pass(this) :: nbwait_vec => gs_nbwait_vec_mpi
80 end type gs_mpi_t
81
82contains
83
86 subroutine gs_mpi_init(this, send_pe, recv_pe)
87 class(gs_mpi_t), intent(inout) :: this
88 type(stack_i4_t), intent(inout) :: send_pe
89 type(stack_i4_t), intent(inout) :: recv_pe
90 integer :: i, nsend, nrecv, send_total, recv_total
91
92 call this%init_order(send_pe, recv_pe)
93
94 nsend = size(this%send_pe)
95 nrecv = size(this%recv_pe)
96
97 allocate(this%send_len(nsend), this%send_offset(nsend))
98 allocate(this%send_request(nsend))
99
100 allocate(this%recv_len(nrecv), this%recv_offset(nrecv))
101 allocate(this%recv_request(nrecv))
102 allocate(this%recv_indices(nrecv), this%recv_statuses(nrecv))
103
104 send_total = 0
105 do i = 1, nsend
106 this%send_len(i) = this%send_dof(this%send_pe(i))%size()
107 this%send_offset(i) = send_total
108 send_total = send_total + this%send_len(i)
109 end do
110 allocate(this%send_buf(max(1, send_total)))
111
112 recv_total = 0
113 do i = 1, nrecv
114 this%recv_len(i) = this%recv_dof(this%recv_pe(i))%size()
115 this%recv_offset(i) = recv_total
116 recv_total = recv_total + this%recv_len(i)
117 end do
118 allocate(this%recv_buf(max(1, recv_total)))
119
120 ! Fused vector exchange buffers, sized for GS_VEC_NC components.
121 allocate(this%send_buf_v(max(1, gs_vec_nc*send_total)))
122 allocate(this%recv_buf_v(max(1, gs_vec_nc*recv_total)))
123 this%vec_supported = .true.
124
125 end subroutine gs_mpi_init
126
128 subroutine gs_mpi_free(this)
129 class(gs_mpi_t), intent(inout) :: this
130
131 if (allocated(this%send_buf)) then
132 deallocate(this%send_buf)
133 end if
134
135 if (allocated(this%recv_buf)) then
136 deallocate(this%recv_buf)
137 end if
138
139 if (allocated(this%send_len)) then
140 deallocate(this%send_len)
141 end if
142
143 if (allocated(this%recv_len)) then
144 deallocate(this%recv_len)
145 end if
146
147 if (allocated(this%send_offset)) then
148 deallocate(this%send_offset)
149 end if
150
151 if (allocated(this%recv_offset)) then
152 deallocate(this%recv_offset)
153 end if
154
155 if (allocated(this%send_request)) then
156 deallocate(this%send_request)
157 end if
158
159 if (allocated(this%recv_request)) then
160 deallocate(this%recv_request)
161 end if
162
163 if (allocated(this%recv_indices)) then
164 deallocate(this%recv_indices)
165 end if
166
167 if (allocated(this%recv_statuses)) then
168 deallocate(this%recv_statuses)
169 end if
170
171 if (allocated(this%send_buf_v)) then
172 deallocate(this%send_buf_v)
173 end if
174
175 if (allocated(this%recv_buf_v)) then
176 deallocate(this%recv_buf_v)
177 end if
178
179 call this%free_order()
180 call this%free_dofs()
181
182 end subroutine gs_mpi_free
183
185 subroutine gs_nbsend_mpi(this, u, n, tag, deps, strm)
186 class(gs_mpi_t), intent(inout) :: this
187 integer, intent(in) :: n
188 real(kind=rp), dimension(n), intent(inout) :: u
189 integer, intent(in) :: tag
190 type(c_ptr), intent(inout) :: deps
191 type(c_ptr), intent(inout) :: strm
192 integer :: i, j, ierr, dst, off, ndst
193
194 ! Gather data from u into the per-peer slab of send_buf according
195 ! to indices in send_dof. Slabs are contiguous so each MPI_Isend
196 ! sends a single contiguous block.
197
198 ! If the MPI library doesn't support MULTIPLE, use all threads to
199 ! pack the send buffer. Otherwise, let each thread pack and send
200 ! to a different neighbour
201 if (neko_mpi_thread_provided .lt. mpi_thread_multiple) then
202 do i = 1, size(this%send_pe)
203 dst = this%send_pe(i)
204 off = this%send_offset(i)
205 ndst = this%send_len(i)
206 select type (sp => this%send_dof(dst)%data)
207 type is (integer)
208 !$omp do
209 do j = 1, ndst
210 this%send_buf(off + j) = u(sp(j))
211 end do
212 !$omp end do
213 end select
214 !$omp master
215 call mpi_isend(this%send_buf(off + 1), ndst, &
216 mpi_real_precision, dst, tag, &
217 neko_comm, this%send_request(i), ierr)
218 !$omp end master
219 end do
220 !$omp barrier
221 else
222 !$omp do
223 do i = 1, size(this%send_pe)
224 dst = this%send_pe(i)
225 off = this%send_offset(i)
226 ndst = this%send_len(i)
227 select type (sp => this%send_dof(dst)%data)
228 type is (integer)
229 !$omp simd
230 do j = 1, ndst
231 this%send_buf(off + j) = u(sp(j))
232 end do
233 end select
234 call mpi_isend(this%send_buf(off + 1), ndst, &
235 mpi_real_precision, dst, tag, &
236 neko_comm, this%send_request(i), ierr)
237 end do
238 !$omp end do
239 end if
240
241 end subroutine gs_nbsend_mpi
242
244 subroutine gs_nbrecv_mpi(this, tag)
245 class(gs_mpi_t), intent(inout) :: this
246 integer, intent(in) :: tag
247 integer :: i, ierr, off, nsrc
248
249 ! Issue recv requests, we will later check that these have finished
250 ! in nbwait
251
252
253 ! If the MPI library doesn't support MULTIPLE, the master thread
254 ! will issue all Irecv's. Otherwise, threads will issue Irecv's
255 ! concurrently.
256 if (neko_mpi_thread_provided .lt. mpi_thread_multiple) then
257 !$omp master
258 do i = 1, size(this%recv_pe)
259 off = this%recv_offset(i)
260 nsrc = this%recv_len(i)
261 call mpi_irecv(this%recv_buf(off + 1), nsrc, &
262 mpi_real_precision, this%recv_pe(i), tag, &
263 neko_comm, this%recv_request(i), ierr)
264 end do
265 !$omp end master
266 !$omp barrier
267 else
268 !$omp do
269 do i = 1, size(this%recv_pe)
270 off = this%recv_offset(i)
271 nsrc = this%recv_len(i)
272 call mpi_irecv(this%recv_buf(off + 1), nsrc, &
273 mpi_real_precision, this%recv_pe(i), tag, &
274 neko_comm, this%recv_request(i), ierr)
275 end do
276 !$omp end do
277 end if
278 end subroutine gs_nbrecv_mpi
279
281 subroutine gs_nbwait_mpi(this, u, n, op, strm)
282 class(gs_mpi_t), intent(inout) :: this
283 integer, intent(in) :: n
284 real(kind=rp), dimension(n), intent(inout) :: u
285 type(c_ptr), intent(inout) :: strm
286 integer :: i, j, k, src, off, nsrc, ierr
287 integer :: op
288 integer :: nreqs
289 logical :: sends_done
290
291 ! Poll for any subset of the outstanding recv requests to complete,
292 ! reduce each completed slab into u, and repeat until all are done.
293 nreqs = size(this%recv_pe)
294 do while (nreqs .gt. 0)
295 !$omp master
296 call mpi_testsome(size(this%recv_request), this%recv_request, &
297 this%ncompleted, this%recv_indices, this%recv_statuses, ierr)
298 !$omp end master
299 !$omp barrier
300 do k = 1, this%ncompleted
301 i = this%recv_indices(k)
303 src = this%recv_pe(i)
304 off = this%recv_offset(i)
305 nsrc = this%recv_len(i)
306 select type (sp => this%recv_dof(src)%data)
307 type is (integer)
308 ! Do operation with data in buffer on dof specified by recv_dof
309 select case (op)
310 case (gs_op_add)
311 !OCL NORECURRENCE, NOVREC, NOALIAS
312 !DIR$ CONCURRENT
313 !DIR$ IVDEP
314 !GCC$ ivdep
315 !NEC$ IVDEP
316 !$omp do
317 do j = 1, nsrc
318 u(sp(j)) = u(sp(j)) + this%recv_buf(off + j)
319 end do
320 !$omp end do
321 case (gs_op_mul)
322 !OCL NORECURRENCE, NOVREC, NOALIAS
323 !DIR$ CONCURRENT
324 !DIR$ IVDEP
325 !GCC$ ivdep
326 !NEC$ IVDEP
327 !$omp do
328 do j = 1, nsrc
329 u(sp(j)) = u(sp(j)) * this%recv_buf(off + j)
330 end do
331 !$omp end do
332 case (gs_op_min)
333 !OCL NORECURRENCE, NOVREC, NOALIAS
334 !DIR$ CONCURRENT
335 !DIR$ IVDEP
336 !GCC$ ivdep
337 !NEC$ IVDEP
338 !$omp do
339 do j = 1, nsrc
340 u(sp(j)) = min(u(sp(j)), this%recv_buf(off + j))
341 end do
342 !$omp end do
343 case (gs_op_max)
344 !OCL NORECURRENCE, NOVREC, NOALIAS
345 !DIR$ CONCURRENT
346 !DIR$ IVDEP
347 !GCC$ ivdep
348 !NEC$ IVDEP
349 !$omp do
350 do j = 1, nsrc
351 u(sp(j)) = max(u(sp(j)), this%recv_buf(off + j))
352 end do
353 !$omp end do
354 case default
355 call neko_error("Unknown operation in gs_nbwait_mpi")
356 end select
357 end select
358 end do
359 nreqs = nreqs - this%ncompleted
360 ! Synchronise before master can re-enter MPI_Testsome and overwrite
361 ! this%ncompleted / this%recv_indices, which non-master threads are
362 ! still reading in the unpack above.
363 !$omp barrier
364 end do
365 !$omp master
366 ! Finally, poll until all outstanding non-blocking sends have drained.
367 if (size(this%send_request) .gt. 0) then
368 sends_done = .false.
369 do while (.not. sends_done)
370 call mpi_testall(size(this%send_request), this%send_request, &
371 sends_done, mpi_statuses_ignore, ierr)
372 end do
373 end if
374 !$omp end master
375 !$omp barrier
376
377 end subroutine gs_nbwait_mpi
378
383 subroutine gs_nbsend_vec_mpi(this, u, n, nc, tag, deps, strm)
384 class(gs_mpi_t), intent(inout) :: this
385 integer, intent(in) :: n, nc
386 real(kind=rp), dimension(nc*n), intent(inout) :: u
387 integer, intent(in) :: tag
388 type(c_ptr), intent(inout) :: deps
389 type(c_ptr), intent(inout) :: strm
390 integer :: i, j, c, ierr, dst, off, ndst
391
392 if (neko_mpi_thread_provided .lt. mpi_thread_multiple) then
393 do i = 1, size(this%send_pe)
394 dst = this%send_pe(i)
395 off = this%send_offset(i)
396 ndst = this%send_len(i)
397 select type (sp => this%send_dof(dst)%data)
398 type is (integer)
399 !$omp do
400 do j = 1, ndst
401 do c = 1, nc
402 this%send_buf_v(nc*off + (c-1)*ndst + j) = &
403 u((c-1)*n + sp(j))
404 end do
405 end do
406 !$omp end do
407 end select
408 !$omp master
409 call mpi_isend(this%send_buf_v(nc*off + 1), nc*ndst, &
410 mpi_real_precision, dst, tag, &
411 neko_comm, this%send_request(i), ierr)
412 !$omp end master
413 end do
414 !$omp barrier
415 else
416 !$omp do
417 do i = 1, size(this%send_pe)
418 dst = this%send_pe(i)
419 off = this%send_offset(i)
420 ndst = this%send_len(i)
421 select type (sp => this%send_dof(dst)%data)
422 type is (integer)
423 do c = 1, nc
424 !$omp simd
425 do j = 1, ndst
426 this%send_buf_v(nc*off + (c-1)*ndst + j) = &
427 u((c-1)*n + sp(j))
428 end do
429 end do
430 end select
431 call mpi_isend(this%send_buf_v(nc*off + 1), nc*ndst, &
432 mpi_real_precision, dst, tag, &
433 neko_comm, this%send_request(i), ierr)
434 end do
435 !$omp end do
436 end if
437
438 end subroutine gs_nbsend_vec_mpi
439
441 subroutine gs_nbrecv_vec_mpi(this, tag, nc)
442 class(gs_mpi_t), intent(inout) :: this
443 integer, intent(in) :: tag, nc
444 integer :: i, ierr, off, nsrc
445
446 if (neko_mpi_thread_provided .lt. mpi_thread_multiple) then
447 !$omp master
448 do i = 1, size(this%recv_pe)
449 off = this%recv_offset(i)
450 nsrc = this%recv_len(i)
451 call mpi_irecv(this%recv_buf_v(nc*off + 1), nc*nsrc, &
452 mpi_real_precision, this%recv_pe(i), tag, &
453 neko_comm, this%recv_request(i), ierr)
454 end do
455 !$omp end master
456 !$omp barrier
457 else
458 !$omp do
459 do i = 1, size(this%recv_pe)
460 off = this%recv_offset(i)
461 nsrc = this%recv_len(i)
462 call mpi_irecv(this%recv_buf_v(nc*off + 1), nc*nsrc, &
463 mpi_real_precision, this%recv_pe(i), tag, &
464 neko_comm, this%recv_request(i), ierr)
465 end do
466 !$omp end do
467 end if
468 end subroutine gs_nbrecv_vec_mpi
469
471 subroutine gs_nbwait_vec_mpi(this, u, n, nc, op, strm)
472 class(gs_mpi_t), intent(inout) :: this
473 integer, intent(in) :: n, nc
474 real(kind=rp), dimension(nc*n), intent(inout) :: u
475 type(c_ptr), intent(inout) :: strm
476 integer :: i, j, c, k, src, off, nsrc, ierr
477 integer :: op
478 integer :: nreqs
479 logical :: sends_done
480
481 nreqs = size(this%recv_pe)
482 do while (nreqs .gt. 0)
483 !$omp master
484 call mpi_testsome(size(this%recv_request), this%recv_request, &
485 this%ncompleted, this%recv_indices, this%recv_statuses, ierr)
486 !$omp end master
487 !$omp barrier
488 do k = 1, this%ncompleted
489 i = this%recv_indices(k)
490 src = this%recv_pe(i)
491 off = this%recv_offset(i)
492 nsrc = this%recv_len(i)
493 select type (sp => this%recv_dof(src)%data)
494 type is (integer)
495 select case (op)
496 case (gs_op_add)
497 !$omp do
498 do j = 1, nsrc
499 do c = 1, nc
500 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
501 this%recv_buf_v(nc*off + (c-1)*nsrc + j)
502 end do
503 end do
504 !$omp end do
505 case (gs_op_mul)
506 !$omp do
507 do j = 1, nsrc
508 do c = 1, nc
509 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
510 this%recv_buf_v(nc*off + (c-1)*nsrc + j)
511 end do
512 end do
513 !$omp end do
514 case (gs_op_min)
515 !$omp do
516 do j = 1, nsrc
517 do c = 1, nc
518 u((c-1)*n + sp(j)) = min(u((c-1)*n + sp(j)), &
519 this%recv_buf_v(nc*off + (c-1)*nsrc + j))
520 end do
521 end do
522 !$omp end do
523 case (gs_op_max)
524 !$omp do
525 do j = 1, nsrc
526 do c = 1, nc
527 u((c-1)*n + sp(j)) = max(u((c-1)*n + sp(j)), &
528 this%recv_buf_v(nc*off + (c-1)*nsrc + j))
529 end do
530 end do
531 !$omp end do
532 case default
533 call neko_error("Unknown operation in gs_nbwait_vec_mpi")
534 end select
535 end select
536 end do
537 nreqs = nreqs - this%ncompleted
538 !$omp barrier
539 end do
540 !$omp master
541 if (size(this%send_request) .gt. 0) then
542 sends_done = .false.
543 do while (.not. sends_done)
544 call mpi_testall(size(this%send_request), this%send_request, &
545 sends_done, mpi_statuses_ignore, ierr)
546 end do
547 end if
548 !$omp end master
549 !$omp barrier
550
551 end subroutine gs_nbwait_vec_mpi
552
553end module gs_mpi
Definition comm.F90:1
type(mpi_datatype), public mpi_real_precision
MPI type for working precision of REAL types.
Definition comm.F90:54
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
integer, public neko_mpi_thread_provided
Thread support provided by the MPI library.
Definition comm.F90:74
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
integer, parameter, public gs_comm_mpigpu
Definition gs_comm.f90:43
integer, parameter, public gs_comm_mpi
Definition gs_comm.f90:43
Defines MPI gather-scatter communication.
Definition gs_mpi.f90:34
subroutine gs_nbwait_vec_mpi(this, u, n, nc, op, strm)
Wait for a fused nc-component exchange and reduce each received slab.
Definition gs_mpi.f90:472
subroutine gs_nbwait_mpi(this, u, n, op, strm)
Wait for non-blocking operations.
Definition gs_mpi.f90:282
subroutine gs_mpi_init(this, send_pe, recv_pe)
Initialise MPI based communication method See gs_comm.f90 for details.
Definition gs_mpi.f90:87
subroutine gs_mpi_free(this)
Deallocate MPI based communication method.
Definition gs_mpi.f90:129
subroutine gs_nbrecv_vec_mpi(this, tag, nc)
Post non-blocking receives for a fused nc-component exchange.
Definition gs_mpi.f90:442
subroutine gs_nbsend_vec_mpi(this, u, n, nc, tag, deps, strm)
Post non-blocking sends for a fused nc-component exchange.
Definition gs_mpi.f90:384
subroutine gs_nbsend_mpi(this, u, n, tag, deps, strm)
Post non-blocking send operations.
Definition gs_mpi.f90:186
subroutine gs_nbrecv_mpi(this, tag)
Post non-blocking receive operations.
Definition gs_mpi.f90:245
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 sp
Definition num_types.f90:8
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:52
Gather-scatter communication using MPI.
Definition gs_mpi.f90:49
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40