Neko 1.99.5
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 !GCC$ ivdep
314 !NEC$ IVDEP
315 !$omp do
316 do j = 1, nsrc
317 u(sp(j)) = u(sp(j)) + this%recv_buf(off + j)
318 end do
319 !$omp end do
320 case (gs_op_mul)
321 !OCL NORECURRENCE, NOVREC, NOALIAS
322 !DIR$ CONCURRENT
323 !GCC$ ivdep
324 !NEC$ IVDEP
325 !$omp do
326 do j = 1, nsrc
327 u(sp(j)) = u(sp(j)) * this%recv_buf(off + j)
328 end do
329 !$omp end do
330 case (gs_op_min)
331 !OCL NORECURRENCE, NOVREC, NOALIAS
332 !DIR$ CONCURRENT
333 !GCC$ ivdep
334 !NEC$ IVDEP
335 !$omp do
336 do j = 1, nsrc
337 u(sp(j)) = min(u(sp(j)), this%recv_buf(off + j))
338 end do
339 !$omp end do
340 case (gs_op_max)
341 !OCL NORECURRENCE, NOVREC, NOALIAS
342 !DIR$ CONCURRENT
343 !GCC$ ivdep
344 !NEC$ IVDEP
345 !$omp do
346 do j = 1, nsrc
347 u(sp(j)) = max(u(sp(j)), this%recv_buf(off + j))
348 end do
349 !$omp end do
350 case default
351 call neko_error("Unknown operation in gs_nbwait_mpi")
352 end select
353 end select
354 end do
355 nreqs = nreqs - this%ncompleted
356 ! Synchronise before master can re-enter MPI_Testsome and overwrite
357 ! this%ncompleted / this%recv_indices, which non-master threads are
358 ! still reading in the unpack above.
359 !$omp barrier
360 end do
361 !$omp master
362 ! Finally, poll until all outstanding non-blocking sends have drained.
363 if (size(this%send_request) .gt. 0) then
364 sends_done = .false.
365 do while (.not. sends_done)
366 call mpi_testall(size(this%send_request), this%send_request, &
367 sends_done, mpi_statuses_ignore, ierr)
368 end do
369 end if
370 !$omp end master
371 !$omp barrier
372
373 end subroutine gs_nbwait_mpi
374
379 subroutine gs_nbsend_vec_mpi(this, u, n, nc, tag, deps, strm)
380 class(gs_mpi_t), intent(inout) :: this
381 integer, intent(in) :: n, nc
382 real(kind=rp), dimension(nc*n), intent(inout) :: u
383 integer, intent(in) :: tag
384 type(c_ptr), intent(inout) :: deps
385 type(c_ptr), intent(inout) :: strm
386 integer :: i, j, c, ierr, dst, off, ndst
387
388 if (neko_mpi_thread_provided .lt. mpi_thread_multiple) then
389 do i = 1, size(this%send_pe)
390 dst = this%send_pe(i)
391 off = this%send_offset(i)
392 ndst = this%send_len(i)
393 select type (sp => this%send_dof(dst)%data)
394 type is (integer)
395 !$omp do
396 do j = 1, ndst
397 do c = 1, nc
398 this%send_buf_v(nc*off + (c-1)*ndst + j) = &
399 u((c-1)*n + sp(j))
400 end do
401 end do
402 !$omp end do
403 end select
404 !$omp master
405 call mpi_isend(this%send_buf_v(nc*off + 1), nc*ndst, &
406 mpi_real_precision, dst, tag, &
407 neko_comm, this%send_request(i), ierr)
408 !$omp end master
409 end do
410 !$omp barrier
411 else
412 !$omp do
413 do i = 1, size(this%send_pe)
414 dst = this%send_pe(i)
415 off = this%send_offset(i)
416 ndst = this%send_len(i)
417 select type (sp => this%send_dof(dst)%data)
418 type is (integer)
419 do c = 1, nc
420 !$omp simd
421 do j = 1, ndst
422 this%send_buf_v(nc*off + (c-1)*ndst + j) = &
423 u((c-1)*n + sp(j))
424 end do
425 end do
426 end select
427 call mpi_isend(this%send_buf_v(nc*off + 1), nc*ndst, &
428 mpi_real_precision, dst, tag, &
429 neko_comm, this%send_request(i), ierr)
430 end do
431 !$omp end do
432 end if
433
434 end subroutine gs_nbsend_vec_mpi
435
437 subroutine gs_nbrecv_vec_mpi(this, tag, nc)
438 class(gs_mpi_t), intent(inout) :: this
439 integer, intent(in) :: tag, nc
440 integer :: i, ierr, off, nsrc
441
442 if (neko_mpi_thread_provided .lt. mpi_thread_multiple) then
443 !$omp master
444 do i = 1, size(this%recv_pe)
445 off = this%recv_offset(i)
446 nsrc = this%recv_len(i)
447 call mpi_irecv(this%recv_buf_v(nc*off + 1), nc*nsrc, &
448 mpi_real_precision, this%recv_pe(i), tag, &
449 neko_comm, this%recv_request(i), ierr)
450 end do
451 !$omp end master
452 !$omp barrier
453 else
454 !$omp do
455 do i = 1, size(this%recv_pe)
456 off = this%recv_offset(i)
457 nsrc = this%recv_len(i)
458 call mpi_irecv(this%recv_buf_v(nc*off + 1), nc*nsrc, &
459 mpi_real_precision, this%recv_pe(i), tag, &
460 neko_comm, this%recv_request(i), ierr)
461 end do
462 !$omp end do
463 end if
464 end subroutine gs_nbrecv_vec_mpi
465
467 subroutine gs_nbwait_vec_mpi(this, u, n, nc, op, strm)
468 class(gs_mpi_t), intent(inout) :: this
469 integer, intent(in) :: n, nc
470 real(kind=rp), dimension(nc*n), intent(inout) :: u
471 type(c_ptr), intent(inout) :: strm
472 integer :: i, j, c, k, src, off, nsrc, ierr
473 integer :: op
474 integer :: nreqs
475 logical :: sends_done
476
477 nreqs = size(this%recv_pe)
478 do while (nreqs .gt. 0)
479 !$omp master
480 call mpi_testsome(size(this%recv_request), this%recv_request, &
481 this%ncompleted, this%recv_indices, this%recv_statuses, ierr)
482 !$omp end master
483 !$omp barrier
484 do k = 1, this%ncompleted
485 i = this%recv_indices(k)
486 src = this%recv_pe(i)
487 off = this%recv_offset(i)
488 nsrc = this%recv_len(i)
489 select type (sp => this%recv_dof(src)%data)
490 type is (integer)
491 select case (op)
492 case (gs_op_add)
493 !$omp do
494 do j = 1, nsrc
495 do c = 1, nc
496 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) + &
497 this%recv_buf_v(nc*off + (c-1)*nsrc + j)
498 end do
499 end do
500 !$omp end do
501 case (gs_op_mul)
502 !$omp do
503 do j = 1, nsrc
504 do c = 1, nc
505 u((c-1)*n + sp(j)) = u((c-1)*n + sp(j)) * &
506 this%recv_buf_v(nc*off + (c-1)*nsrc + j)
507 end do
508 end do
509 !$omp end do
510 case (gs_op_min)
511 !$omp do
512 do j = 1, nsrc
513 do c = 1, nc
514 u((c-1)*n + sp(j)) = min(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_max)
520 !$omp do
521 do j = 1, nsrc
522 do c = 1, nc
523 u((c-1)*n + sp(j)) = max(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 default
529 call neko_error("Unknown operation in gs_nbwait_vec_mpi")
530 end select
531 end select
532 end do
533 nreqs = nreqs - this%ncompleted
534 !$omp barrier
535 end do
536 !$omp master
537 if (size(this%send_request) .gt. 0) then
538 sends_done = .false.
539 do while (.not. sends_done)
540 call mpi_testall(size(this%send_request), this%send_request, &
541 sends_done, mpi_statuses_ignore, ierr)
542 end do
543 end if
544 !$omp end master
545 !$omp barrier
546
547 end subroutine gs_nbwait_vec_mpi
548
549end 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:53
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:45
integer, public neko_mpi_thread_provided
Thread support provided by the MPI library.
Definition comm.F90:73
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:468
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:438
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:380
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