Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_device_crystal.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!
47 use num_types, only : rp, c_rp
48 use gs_comm, only : gs_comm_t, gs_vec_nc
50 use stack, only : stack_i4_t
51 use htable, only : htable_i4_t
54 use utils, only : neko_error
55 use, intrinsic :: iso_c_binding, only : c_ptr, c_null_ptr, c_associated, &
56 c_sizeof, c_size_t, c_int32_t
57 implicit none
58 private
59
61 type, public, extends(gs_comm_t) :: gs_device_crystal_t
63 type(gs_crystal_plan_t) :: plan
65 type(c_ptr) :: buf_d(2) = c_null_ptr
67 type(c_ptr) :: sbuf_d = c_null_ptr
70 type(c_ptr) :: buf_v_d(2) = c_null_ptr
71 type(c_ptr) :: sbuf_v_d = c_null_ptr
74 type(c_ptr), allocatable :: keep_idx_d(:), send_idx_d(:)
77 type(c_ptr), allocatable :: keep_idx_v_d(:), send_idx_v_d(:)
79 type(c_ptr) :: pack_keep_d = c_null_ptr
80 type(c_ptr) :: pack_send_d = c_null_ptr
81 type(c_ptr) :: unpack_d = c_null_ptr
83 type(c_ptr) :: sreqs = c_null_ptr
84 type(c_ptr) :: rreqs = c_null_ptr
85 integer :: nsreq = 0, nrreq = 0
88 integer :: tag = 0
91 integer :: vec_nc = 0
92 contains
93 procedure, pass(this) :: init => gs_device_crystal_init
94 procedure, pass(this) :: free => gs_device_crystal_free
95 procedure, pass(this) :: nbsend => gs_device_crystal_nbsend
96 procedure, pass(this) :: nbrecv => gs_device_crystal_nbrecv
97 procedure, pass(this) :: nbwait => gs_device_crystal_nbwait
98 procedure, pass(this) :: init_vec => gs_device_crystal_init_vec
99 procedure, pass(this) :: nbsend_vec => gs_device_crystal_nbsend_vec
100 procedure, pass(this) :: nbrecv_vec => gs_device_crystal_nbrecv_vec
101 procedure, pass(this) :: nbwait_vec => gs_device_crystal_nbwait_vec
102 end type gs_device_crystal_t
103
104#ifdef HAVE_HIP
105 interface
106 subroutine hip_gs_pack(u_d, buf_d, dof_d, offset, n, stream) &
107 bind(c, name = 'hip_gs_pack')
108 use, intrinsic :: iso_c_binding
109 implicit none
110 integer(c_int), value :: n, offset
111 type(c_ptr), value :: u_d, buf_d, dof_d, stream
112 end subroutine hip_gs_pack
113 end interface
114
115 interface
116 subroutine hip_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
117 bind(c, name = 'hip_gs_unpack')
118 use, intrinsic :: iso_c_binding
119 implicit none
120 integer(c_int), value :: op, offset, n
121 type(c_ptr), value :: u_d, buf_d, dof_d, stream
122 end subroutine hip_gs_unpack
123 end interface
124
125 interface
126 subroutine hip_gs_pack_vec(u_d, buf_d, dof_d, offset, n, nc, ns, stream) &
127 bind(c, name = 'hip_gs_pack_vec')
128 use, intrinsic :: iso_c_binding
129 implicit none
130 integer(c_int), value :: offset, n, nc, ns
131 type(c_ptr), value :: u_d, buf_d, dof_d, stream
132 end subroutine hip_gs_pack_vec
133 end interface
134
135 interface
136 subroutine hip_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
137 stream) bind(c, name = 'hip_gs_unpack_vec')
138 use, intrinsic :: iso_c_binding
139 implicit none
140 integer(c_int), value :: op, offset, n, nc, ns
141 type(c_ptr), value :: u_d, buf_d, dof_d, stream
142 end subroutine hip_gs_unpack_vec
143 end interface
144#elif HAVE_CUDA
145 interface
146 subroutine cuda_gs_pack(u_d, buf_d, dof_d, offset, n, stream) &
147 bind(c, name = 'cuda_gs_pack')
148 use, intrinsic :: iso_c_binding
149 implicit none
150 integer(c_int), value :: n, offset
151 type(c_ptr), value :: u_d, buf_d, dof_d, stream
152 end subroutine cuda_gs_pack
153 end interface
154
155 interface
156 subroutine cuda_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
157 bind(c, name = 'cuda_gs_unpack')
158 use, intrinsic :: iso_c_binding
159 implicit none
160 integer(c_int), value :: op, offset, n
161 type(c_ptr), value :: u_d, buf_d, dof_d, stream
162 end subroutine cuda_gs_unpack
163 end interface
164
165 interface
166 subroutine cuda_gs_pack_vec(u_d, buf_d, dof_d, offset, n, nc, ns, stream) &
167 bind(c, name = 'cuda_gs_pack_vec')
168 use, intrinsic :: iso_c_binding
169 implicit none
170 integer(c_int), value :: offset, n, nc, ns
171 type(c_ptr), value :: u_d, buf_d, dof_d, stream
172 end subroutine cuda_gs_pack_vec
173 end interface
174
175 interface
176 subroutine cuda_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
177 stream) bind(c, name = 'cuda_gs_unpack_vec')
178 use, intrinsic :: iso_c_binding
179 implicit none
180 integer(c_int), value :: op, offset, n, nc, ns
181 type(c_ptr), value :: u_d, buf_d, dof_d, stream
182 end subroutine cuda_gs_unpack_vec
183 end interface
184#endif
185
186 interface
187 subroutine device_mpi_init_reqs(n, reqs) &
188 bind(c, name = 'device_mpi_init_reqs')
189 use, intrinsic :: iso_c_binding
190 implicit none
191 integer(c_int), value :: n
192 type(c_ptr) :: reqs
193 end subroutine device_mpi_init_reqs
194 end interface
195
196 interface
197 subroutine device_mpi_free_reqs(reqs) &
198 bind(c, name = 'device_mpi_free_reqs')
199 use, intrinsic :: iso_c_binding
200 implicit none
201 type(c_ptr) :: reqs
202 end subroutine device_mpi_free_reqs
203 end interface
204
205 interface
206 subroutine device_mpi_isend(buf_d, offset, nbytes, rank, tag, reqs, i) &
207 bind(c, name = 'device_mpi_isend')
208 use, intrinsic :: iso_c_binding
209 implicit none
210 integer(c_int), value :: offset, nbytes, rank, tag, i
211 type(c_ptr), value :: buf_d, reqs
212 end subroutine device_mpi_isend
213 end interface
214
215 interface
216 subroutine device_mpi_irecv(buf_d, offset, nbytes, rank, tag, reqs, i) &
217 bind(c, name = 'device_mpi_irecv')
218 use, intrinsic :: iso_c_binding
219 implicit none
220 integer(c_int), value :: offset, nbytes, rank, tag, i
221 type(c_ptr), value :: buf_d, reqs
222 end subroutine device_mpi_irecv
223 end interface
224
225 interface
226 subroutine device_mpi_waitall(n, reqs) &
227 bind(c, name = 'device_mpi_waitall')
228 use, intrinsic :: iso_c_binding
229 implicit none
230 integer(c_int), value :: n
231 type(c_ptr), value :: reqs
232 end subroutine device_mpi_waitall
233 end interface
234
235contains
236
239 subroutine gs_device_crystal_init(this, send_pe, recv_pe)
240 class(gs_device_crystal_t), intent(inout) :: this
241 type(stack_i4_t), intent(inout) :: send_pe
242 type(stack_i4_t), intent(inout) :: recv_pe
243 integer, allocatable :: dofs(:)
244 integer :: i, nst
245 integer(c_size_t) :: sz
246 real(c_rp) :: rp_dummy
247
248 call this%init_order(send_pe, recv_pe)
249
250 call this%plan%init(this%send_pe, this%recv_pe, this%send_dof, &
251 this%recv_dof)
252
253 call device_mpi_init_reqs(1, this%sreqs)
254 call device_mpi_init_reqs(2, this%rreqs)
255
256 sz = c_sizeof(rp_dummy) * this%plan%nwrk
257 call device_alloc(this%buf_d(1), sz)
258 call device_alloc(this%buf_d(2), sz)
259 sz = c_sizeof(rp_dummy) * this%plan%nsmax
260 call device_alloc(this%sbuf_d, sz)
261
262 ! The first stage gathers straight out of the shared vector
263 if (this%plan%nstage .gt. 0) then
264 call cr_upload(this%pack_keep_d, this%plan%pack_keep_dof, &
265 this%plan%stage(1)%nkw)
266 call cr_upload(this%pack_send_d, this%plan%pack_send_dof, &
267 this%plan%stage(1)%nsw)
268 end if
269
270 ! A shared dof may be delivered by several peers, so the reduction
271 ! marks the repeats for the unpack kernel to handle atomically
272 allocate(dofs(max(this%plan%nfinal, 1)))
273 call cr_mark_dupes(this%plan%unpack_dof, dofs, this%plan%nfinal)
274 call cr_upload(this%unpack_d, dofs, this%plan%nfinal)
275 deallocate(dofs)
276
277 nst = max(this%plan%nstage, 1)
278 allocate(this%keep_idx_d(nst), this%send_idx_d(nst))
279 allocate(this%keep_idx_v_d(nst), this%send_idx_v_d(nst))
280 this%keep_idx_d = c_null_ptr
281 this%send_idx_d = c_null_ptr
282 this%keep_idx_v_d = c_null_ptr
283 this%send_idx_v_d = c_null_ptr
284
285 ! Stage one has no index lists: its words come from the shared vector
286 do i = 2, this%plan%nstage
287 associate(st => this%plan%stage(i))
288 if (.not. st%inplace) then
289 call cr_upload(this%keep_idx_d(i), st%keep_idx, st%nkw)
290 end if
291 if (st%dst .ge. 0) then
292 call cr_upload(this%send_idx_d(i), st%send_idx, st%nsw)
293 end if
294 end associate
295 end do
296
297 this%vec_supported = .true.
298 this%vec_ready = .false.
299
300 end subroutine gs_device_crystal_init
301
308 class(gs_device_crystal_t), intent(inout) :: this
309 integer(c_size_t) :: sz
310 real(c_rp) :: rp_dummy
311
312 sz = c_sizeof(rp_dummy) * gs_vec_nc * this%plan%nwrk
313 call device_alloc(this%buf_v_d(1), sz)
314 call device_alloc(this%buf_v_d(2), sz)
315 sz = c_sizeof(rp_dummy) * gs_vec_nc * this%plan%nsmax
316 call device_alloc(this%sbuf_v_d, sz)
317
318 end subroutine gs_device_crystal_init_vec
319
321 subroutine gs_device_crystal_free(this)
322 class(gs_device_crystal_t), intent(inout) :: this
323 integer :: i
324
325 if (c_associated(this%sreqs)) call device_mpi_free_reqs(this%sreqs)
326 if (c_associated(this%rreqs)) call device_mpi_free_reqs(this%rreqs)
327
328 do i = 1, 2
329 if (c_associated(this%buf_d(i))) call device_free(this%buf_d(i))
330 if (c_associated(this%buf_v_d(i))) call device_free(this%buf_v_d(i))
331 end do
332 if (c_associated(this%sbuf_d)) call device_free(this%sbuf_d)
333 if (c_associated(this%sbuf_v_d)) call device_free(this%sbuf_v_d)
334
335 if (c_associated(this%pack_keep_d)) call device_free(this%pack_keep_d)
336 if (c_associated(this%pack_send_d)) call device_free(this%pack_send_d)
337 if (c_associated(this%unpack_d)) call device_free(this%unpack_d)
338
339 call cr_free_ptrs(this%keep_idx_d)
340 call cr_free_ptrs(this%send_idx_d)
341 call cr_free_ptrs(this%keep_idx_v_d)
342 call cr_free_ptrs(this%send_idx_v_d)
343
344 if (allocated(this%keep_idx_d)) deallocate(this%keep_idx_d)
345 if (allocated(this%send_idx_d)) deallocate(this%send_idx_d)
346 if (allocated(this%keep_idx_v_d)) deallocate(this%keep_idx_v_d)
347 if (allocated(this%send_idx_v_d)) deallocate(this%send_idx_v_d)
348
349 this%vec_nc = 0
350 this%vec_ready = .false.
351
352 call this%plan%free()
353
354 call this%free_order()
355 call this%free_dofs()
356
357 end subroutine gs_device_crystal_free
358
360 subroutine gs_device_crystal_nbrecv(this, tag)
361 class(gs_device_crystal_t), intent(inout) :: this
362 integer, intent(in) :: tag
363
364 this%tag = tag
365 this%nrreq = 0
366 this%nsreq = 0
367 if (this%plan%nstage .eq. 0) return
368
369 associate(st => this%plan%stage(1))
370 if (st%src .ge. 0) then
371 this%nrreq = this%nrreq + 1
372 call device_mpi_irecv(this%buf_d(st%dst_sel), rp*st%nkw, &
373 rp*st%nrw, st%src, tag, this%rreqs, this%nrreq)
374 end if
375 if (st%src2 .ge. 0) then
376 this%nrreq = this%nrreq + 1
377 call device_mpi_irecv(this%buf_d(st%dst_sel), &
378 rp*(st%nkw + st%nrw), rp*st%nr2w, st%src2, tag, &
379 this%rreqs, this%nrreq)
380 end if
381 end associate
382
383 end subroutine gs_device_crystal_nbrecv
384
386 subroutine gs_device_crystal_nbsend(this, u, n, tag, deps, strm)
387 class(gs_device_crystal_t), intent(inout) :: this
388 integer, intent(in) :: n
389 real(kind=rp), dimension(n), intent(inout) :: u
390 integer, intent(in) :: tag
391 type(c_ptr), intent(inout) :: deps
392 type(c_ptr), intent(inout) :: strm
393 type(c_ptr) :: u_d
394
395 if (this%plan%nstage .eq. 0) return
396
397 u_d = device_get_ptr(u)
398
399 associate(st => this%plan%stage(1))
400 if (st%dst .ge. 0) then
401 call cr_gather(u_d, this%sbuf_d, this%pack_send_d, st%nsw, strm)
402 call device_sync(strm)
403 this%nsreq = 1
404 call device_mpi_isend(this%sbuf_d, 0, rp*st%nsw, st%dst, tag, &
405 this%sreqs, 1)
406 end if
407
408 ! The words that stay put, into the column the arriving ones are
409 ! being received behind
410 call cr_gather(u_d, this%buf_d(st%dst_sel), this%pack_keep_d, &
411 st%nkw, strm)
412 end associate
413
414 end subroutine gs_device_crystal_nbsend
415
418 subroutine gs_device_crystal_nbwait(this, u, n, op, strm)
419 class(gs_device_crystal_t), intent(inout) :: this
420 integer, intent(in) :: n
421 real(kind=rp), dimension(n), intent(inout) :: u
422 type(c_ptr), intent(inout) :: strm
423 integer :: op
424 integer :: s
425 type(c_ptr) :: u_d
426
427 if (this%plan%nstage .eq. 0) return
428
429 u_d = device_get_ptr(u)
430
431 call device_mpi_waitall(this%nrreq, this%rreqs)
432 call device_mpi_waitall(this%nsreq, this%sreqs)
433
434 do s = 2, this%plan%nstage
435 associate(st => this%plan%stage(s))
436 this%nrreq = 0
437 this%nsreq = 0
438 if (st%src .ge. 0) then
439 this%nrreq = this%nrreq + 1
440 call device_mpi_irecv(this%buf_d(st%dst_sel), rp*st%nkw, &
441 rp*st%nrw, st%src, this%tag, this%rreqs, this%nrreq)
442 end if
443 if (st%src2 .ge. 0) then
444 this%nrreq = this%nrreq + 1
445 call device_mpi_irecv(this%buf_d(st%dst_sel), &
446 rp*(st%nkw + st%nrw), rp*st%nr2w, st%src2, this%tag, &
447 this%rreqs, this%nrreq)
448 end if
449
450 if (st%dst .ge. 0) then
451 call cr_gather(this%buf_d(st%src_sel), this%sbuf_d, &
452 this%send_idx_d(s), st%nsw, strm)
453 call device_sync(strm)
454 this%nsreq = 1
455 call device_mpi_isend(this%sbuf_d, 0, rp*st%nsw, st%dst, &
456 this%tag, this%sreqs, 1)
457 end if
458
459 ! A stage that sends nothing keeps every word where it is, and is
460 ! receiving into the column it already occupies
461 if (.not. st%inplace) then
462 call cr_gather(this%buf_d(st%src_sel), this%buf_d(st%dst_sel), &
463 this%keep_idx_d(s), st%nkw, strm)
464 end if
465
466 call device_mpi_waitall(this%nrreq, this%rreqs)
467 call device_mpi_waitall(this%nsreq, this%sreqs)
468 call device_sync(strm)
469 end associate
470 end do
471
472 call cr_scatter(u_d, op, this%buf_d(this%plan%final_sel), &
473 this%unpack_d, this%plan%nfinal, strm)
474
475 call device_sync(strm)
476
477 end subroutine gs_device_crystal_nbwait
478
480 subroutine gs_device_crystal_nbrecv_vec(this, tag, nc)
481 class(gs_device_crystal_t), intent(inout) :: this
482 integer, intent(in) :: tag, nc
483
484 if (nc .gt. gs_vec_nc) then
485 call neko_error('gs_device_crystal: too many components in ' // &
486 'vector exchange')
487 end if
488
489 call cr_vec_index(this, nc)
490
491 this%tag = tag
492 this%nrreq = 0
493 this%nsreq = 0
494 if (this%plan%nstage .eq. 0) return
495
496 associate(st => this%plan%stage(1))
497 if (st%src .ge. 0) then
498 this%nrreq = this%nrreq + 1
499 call device_mpi_irecv(this%buf_v_d(st%dst_sel), rp*nc*st%nkw, &
500 rp*nc*st%nrw, st%src, tag, this%rreqs, this%nrreq)
501 end if
502 if (st%src2 .ge. 0) then
503 this%nrreq = this%nrreq + 1
504 call device_mpi_irecv(this%buf_v_d(st%dst_sel), &
505 rp*nc*(st%nkw + st%nrw), rp*nc*st%nr2w, st%src2, tag, &
506 this%rreqs, this%nrreq)
507 end if
508 end associate
509
510 end subroutine gs_device_crystal_nbrecv_vec
511
515 subroutine gs_device_crystal_nbsend_vec(this, u, n, nc, tag, deps, strm)
516 class(gs_device_crystal_t), intent(inout) :: this
517 integer, intent(in) :: n, nc
518 real(kind=rp), dimension(nc*n), intent(inout) :: u
519 integer, intent(in) :: tag
520 type(c_ptr), intent(inout) :: deps
521 type(c_ptr), intent(inout) :: strm
522 type(c_ptr) :: u_d
523
524 if (this%plan%nstage .eq. 0) return
525
526 u_d = device_get_ptr(u)
527
528 associate(st => this%plan%stage(1))
529 if (st%dst .ge. 0) then
530 call cr_gather_vec(u_d, this%sbuf_v_d, this%pack_send_d, st%nsw, &
531 nc, n, strm)
532 call device_sync(strm)
533 this%nsreq = 1
534 call device_mpi_isend(this%sbuf_v_d, 0, rp*nc*st%nsw, st%dst, &
535 tag, this%sreqs, 1)
536 end if
537
538 call cr_gather_vec(u_d, this%buf_v_d(st%dst_sel), this%pack_keep_d, &
539 st%nkw, nc, n, strm)
540 end associate
541
542 end subroutine gs_device_crystal_nbsend_vec
543
546 subroutine gs_device_crystal_nbwait_vec(this, u, n, nc, op, strm)
547 class(gs_device_crystal_t), intent(inout) :: this
548 integer, intent(in) :: n, nc
549 real(kind=rp), dimension(nc*n), intent(inout) :: u
550 type(c_ptr), intent(inout) :: strm
551 integer :: op
552 integer :: s
553 type(c_ptr) :: u_d
554
555 if (this%plan%nstage .eq. 0) return
556
557 u_d = device_get_ptr(u)
558
559 call device_mpi_waitall(this%nrreq, this%rreqs)
560 call device_mpi_waitall(this%nsreq, this%sreqs)
561
562 do s = 2, this%plan%nstage
563 associate(st => this%plan%stage(s))
564 this%nrreq = 0
565 this%nsreq = 0
566 if (st%src .ge. 0) then
567 this%nrreq = this%nrreq + 1
568 call device_mpi_irecv(this%buf_v_d(st%dst_sel), rp*nc*st%nkw, &
569 rp*nc*st%nrw, st%src, this%tag, this%rreqs, this%nrreq)
570 end if
571 if (st%src2 .ge. 0) then
572 this%nrreq = this%nrreq + 1
573 call device_mpi_irecv(this%buf_v_d(st%dst_sel), &
574 rp*nc*(st%nkw + st%nrw), rp*nc*st%nr2w, st%src2, &
575 this%tag, this%rreqs, this%nrreq)
576 end if
577
578 ! The interleaved layout makes a stage's word movement the same
579 ! indexed gather as in the scalar case, over nc times as many
580 ! words, so the plain pack kernel drives it here too
581 if (st%dst .ge. 0) then
582 call cr_gather(this%buf_v_d(st%src_sel), this%sbuf_v_d, &
583 this%send_idx_v_d(s), nc*st%nsw, strm)
584 call device_sync(strm)
585 this%nsreq = 1
586 call device_mpi_isend(this%sbuf_v_d, 0, rp*nc*st%nsw, st%dst, &
587 this%tag, this%sreqs, 1)
588 end if
589
590 if (.not. st%inplace) then
591 call cr_gather(this%buf_v_d(st%src_sel), &
592 this%buf_v_d(st%dst_sel), this%keep_idx_v_d(s), &
593 nc*st%nkw, strm)
594 end if
595
596 call device_mpi_waitall(this%nrreq, this%rreqs)
597 call device_mpi_waitall(this%nsreq, this%sreqs)
598 call device_sync(strm)
599 end associate
600 end do
601
602 call cr_scatter_vec(u_d, op, this%buf_v_d(this%plan%final_sel), &
603 this%unpack_d, this%plan%nfinal, nc, n, strm)
604
605 call device_sync(strm)
606
607 end subroutine gs_device_crystal_nbwait_vec
608
611 subroutine cr_gather(src_d, dst_d, idx_d, n, strm)
612 type(c_ptr), intent(in) :: src_d, dst_d, idx_d
613 integer, intent(in) :: n
614 type(c_ptr), intent(inout) :: strm
615
616 if (n .le. 0) return
617
618#ifdef HAVE_HIP
619 call hip_gs_pack(src_d, dst_d, idx_d, 0, n, strm)
620#elif HAVE_CUDA
621 call cuda_gs_pack(src_d, dst_d, idx_d, 0, n, strm)
622#else
623 call neko_error('gs_device_crystal: no backend')
624#endif
625
626 end subroutine cr_gather
627
630 subroutine cr_gather_vec(src_d, dst_d, idx_d, n, nc, ns, strm)
631 type(c_ptr), intent(in) :: src_d, dst_d, idx_d
632 integer, intent(in) :: n, nc, ns
633 type(c_ptr), intent(inout) :: strm
634
635 if (n .le. 0) return
636
637#ifdef HAVE_HIP
638 call hip_gs_pack_vec(src_d, dst_d, idx_d, 0, n, nc, ns, strm)
639#elif HAVE_CUDA
640 call cuda_gs_pack_vec(src_d, dst_d, idx_d, 0, n, nc, ns, strm)
641#else
642 call neko_error('gs_device_crystal: no backend')
643#endif
644
645 end subroutine cr_gather_vec
646
648 subroutine cr_scatter(u_d, op, buf_d, idx_d, n, strm)
649 type(c_ptr), intent(in) :: u_d, buf_d, idx_d
650 integer, intent(in) :: op, n
651 type(c_ptr), intent(inout) :: strm
652
653 if (n .le. 0) return
654
655#ifdef HAVE_HIP
656 call hip_gs_unpack(u_d, op, buf_d, idx_d, 0, n, strm)
657#elif HAVE_CUDA
658 call cuda_gs_unpack(u_d, op, buf_d, idx_d, 0, n, strm)
659#else
660 call neko_error('gs_device_crystal: no backend')
661#endif
662
663 end subroutine cr_scatter
664
666 subroutine cr_scatter_vec(u_d, op, buf_d, idx_d, n, nc, ns, strm)
667 type(c_ptr), intent(in) :: u_d, buf_d, idx_d
668 integer, intent(in) :: op, n, nc, ns
669 type(c_ptr), intent(inout) :: strm
670
671 if (n .le. 0) return
672
673#ifdef HAVE_HIP
674 call hip_gs_unpack_vec(u_d, op, buf_d, idx_d, 0, n, nc, ns, strm)
675#elif HAVE_CUDA
676 call cuda_gs_unpack_vec(u_d, op, buf_d, idx_d, 0, n, nc, ns, strm)
677#else
678 call neko_error('gs_device_crystal: no backend')
679#endif
680
681 end subroutine cr_scatter_vec
682
688 subroutine cr_vec_index(this, nc)
689 class(gs_device_crystal_t), intent(inout) :: this
690 integer, intent(in) :: nc
691 integer, allocatable :: idx(:)
692 integer :: i
693
694 if (this%vec_nc .eq. nc) return
695
696 call cr_free_ptrs(this%keep_idx_v_d)
697 call cr_free_ptrs(this%send_idx_v_d)
698
699 do i = 2, this%plan%nstage
700 associate(st => this%plan%stage(i))
701 if (.not. st%inplace) then
702 call cr_expand(st%keep_idx, st%nkw, nc, idx)
703 call cr_upload(this%keep_idx_v_d(i), idx, nc*st%nkw)
704 deallocate(idx)
705 end if
706 if (st%dst .ge. 0) then
707 call cr_expand(st%send_idx, st%nsw, nc, idx)
708 call cr_upload(this%send_idx_v_d(i), idx, nc*st%nsw)
709 deallocate(idx)
710 end if
711 end associate
712 end do
713
714 this%vec_nc = nc
715
716 end subroutine cr_vec_index
717
719 subroutine cr_expand(idx, n, nc, out)
720 integer, intent(in) :: idx(:)
721 integer, intent(in) :: n, nc
722 integer, allocatable, intent(out) :: out(:)
723 integer :: j, c
724
725 allocate(out(max(nc*n, 1)))
726 do j = 1, n
727 do c = 1, nc
728 out(nc*(j-1) + c) = nc*(idx(j) - 1) + c
729 end do
730 end do
731
732 end subroutine cr_expand
733
736 subroutine cr_upload(ptr, idx, n)
737 type(c_ptr), intent(inout) :: ptr
738 integer, intent(inout) :: idx(:)
739 integer, intent(in) :: n
740 integer(c_size_t) :: sz
741 integer(c_int32_t) :: i4_dummy
742
743 if (c_associated(ptr)) call device_free(ptr)
744 ptr = c_null_ptr
745
746 if (n .le. 0) return
747
748 sz = c_sizeof(i4_dummy) * n
749 call device_alloc(ptr, sz)
750 call device_memcpy(idx, ptr, n, host_to_device, sync = .true.)
751
752 end subroutine cr_upload
753
755 subroutine cr_free_ptrs(ptrs)
756 type(c_ptr), allocatable, intent(inout) :: ptrs(:)
757 integer :: i
758
759 if (.not. allocated(ptrs)) return
760
761 do i = 1, size(ptrs)
762 if (c_associated(ptrs(i))) call device_free(ptrs(i))
763 ptrs(i) = c_null_ptr
764 end do
765
766 end subroutine cr_free_ptrs
767
770 subroutine cr_mark_dupes(dof, out, n)
771 integer, intent(in) :: dof(:)
772 integer, intent(out) :: out(:)
773 integer, intent(in) :: n
774 type(htable_i4_t) :: doftable
775 integer :: j, dupe, key, val
776
777 if (n .le. 0) return
778
779 ! The table takes its key and data as intent(inout), so neither the
780 ! index list nor the loop counter can be handed to it directly
781 call doftable%init(2*n)
782 do j = 1, n
783 key = dof(j)
784 if (doftable%get(key, dupe) .eq. 0) then
785 if (out(dupe) .gt. 0) out(dupe) = -out(dupe)
786 out(j) = -dof(j)
787 else
788 key = dof(j)
789 val = j
790 call doftable%set(key, val)
791 out(j) = dof(j)
792 end if
793 end do
794 call doftable%free()
795
796 end subroutine cr_mark_dupes
797
798end module gs_device_crystal
void cuda_gs_unpack(real *u_d, int op, real *buf_d, int *dof_d, int offset, int n, cudaStream_t stream)
Definition gs.cu:132
void cuda_gs_pack(void *u_d, void *buf_d, void *dof_d, int offset, int n, cudaStream_t stream)
Definition gs.cu:116
void cuda_gs_pack_vec(void *u_d, void *buf_d, void *dof_d, int offset, int n, int nc, int ns, cudaStream_t stream)
Definition gs.cu:166
void cuda_gs_unpack_vec(real *u_d, int op, real *buf_d, int *dof_d, int offset, int n, int nc, int ns, cudaStream_t stream)
Definition gs.cu:183
Return the device pointer for an associated Fortran array.
Definition device.F90:113
Copy data between host and device (or device and device)
Definition device.F90:72
Synchronize a device or stream.
Definition device.F90:119
Device abstraction, common interface for various accelerators.
Definition device.F90:34
integer, parameter, public host_to_device
Definition device.F90:48
subroutine, public device_free(x_d)
Deallocate memory on the device.
Definition device.F90:243
subroutine, public device_alloc(x_d, s)
Allocate memory on the device.
Definition device.F90:212
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
Routing plan for the crystal router gather-scatter comm. backends.
Defines GPU aware crystal router gather-scatter communication.
subroutine gs_device_crystal_init_vec(this)
Allocate the fused vector working and send buffers in device memory, sized for GS_VEC_NC components....
subroutine gs_device_crystal_free(this)
Deallocate crystal router based device communication.
subroutine gs_device_crystal_nbwait_vec(this, u, n, nc, op, strm)
Drive the remaining routing stages and reduce what is delivered into the shared vector,...
subroutine cr_gather(src_d, dst_d, idx_d, n, strm)
Gather n words of src into dst through the 1-based index list idx_d, i.e. dst(j) = src(idx(j))
subroutine gs_device_crystal_nbrecv(this, tag)
Post the receives of the first routing stage.
subroutine cr_vec_index(this, nc)
Build the per-stage index lists of the fused vector exchange, which address the same words as the sca...
subroutine gs_device_crystal_nbrecv_vec(this, tag, nc)
Post the receives of the first routing stage, fused nc-component.
subroutine cr_upload(ptr, idx, n)
Copy the first n entries of an index list to the device, leaving ptr null when there are none.
subroutine cr_gather_vec(src_d, dst_d, idx_d, n, nc, ns, strm)
Gather n positions of the component-outer src (stride ns) into the interleaved dst.
subroutine cr_scatter(u_d, op, buf_d, idx_d, n, strm)
Reduce n delivered words into the shared vector under op.
subroutine gs_device_crystal_nbwait(this, u, n, op, strm)
Drive the remaining routing stages and reduce what is delivered into the shared vector.
subroutine cr_mark_dupes(dof, out, n)
Copy dof into out, negating every index that appears more than once so that the unpack kernel reduces...
subroutine gs_device_crystal_init(this, send_pe, recv_pe)
Initialise crystal router based device communication See gs_comm.f90 for details.
subroutine cr_scatter_vec(u_d, op, buf_d, idx_d, n, nc, ns, strm)
Reduce n delivered positions into the component-outer shared vector.
subroutine gs_device_crystal_nbsend(this, u, n, tag, deps, strm)
Pack the shared vector and post the send of the first routing stage.
subroutine cr_free_ptrs(ptrs)
Release a list of device pointers, leaving them null.
subroutine gs_device_crystal_nbsend_vec(this, u, n, nc, tag, deps, strm)
Pack the shared vector and post the send of the first routing stage, fused nc-component.
subroutine cr_expand(idx, n, nc, out)
Spell out the components of the 1-based index list idx over nc.
Implements a hash table ADT.
Definition htable.f90:52
integer, parameter, public c_rp
Definition num_types.f90:15
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
The full routing plan for one gather-scatter schedule.
Gather-scatter communication using a crystal router on the device.
Integer based hash table.
Definition htable.f90:102
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40