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