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) :: nbsend_vec => gs_device_crystal_nbsend_vec
99 procedure, pass(this) :: nbrecv_vec => gs_device_crystal_nbrecv_vec
100 procedure, pass(this) :: nbwait_vec => gs_device_crystal_nbwait_vec
101 end type gs_device_crystal_t
102
103#ifdef HAVE_HIP
104 interface
105 subroutine hip_gs_pack(u_d, buf_d, dof_d, offset, n, stream) &
106 bind(c, name = 'hip_gs_pack')
107 use, intrinsic :: iso_c_binding
108 implicit none
109 integer(c_int), value :: n, offset
110 type(c_ptr), value :: u_d, buf_d, dof_d, stream
111 end subroutine hip_gs_pack
112 end interface
113
114 interface
115 subroutine hip_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
116 bind(c, name = 'hip_gs_unpack')
117 use, intrinsic :: iso_c_binding
118 implicit none
119 integer(c_int), value :: op, offset, n
120 type(c_ptr), value :: u_d, buf_d, dof_d, stream
121 end subroutine hip_gs_unpack
122 end interface
123
124 interface
125 subroutine hip_gs_pack_vec(u_d, buf_d, dof_d, offset, n, nc, ns, stream) &
126 bind(c, name = 'hip_gs_pack_vec')
127 use, intrinsic :: iso_c_binding
128 implicit none
129 integer(c_int), value :: offset, n, nc, ns
130 type(c_ptr), value :: u_d, buf_d, dof_d, stream
131 end subroutine hip_gs_pack_vec
132 end interface
133
134 interface
135 subroutine hip_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
136 stream) bind(c, name = 'hip_gs_unpack_vec')
137 use, intrinsic :: iso_c_binding
138 implicit none
139 integer(c_int), value :: op, offset, n, nc, ns
140 type(c_ptr), value :: u_d, buf_d, dof_d, stream
141 end subroutine hip_gs_unpack_vec
142 end interface
143#elif HAVE_CUDA
144 interface
145 subroutine cuda_gs_pack(u_d, buf_d, dof_d, offset, n, stream) &
146 bind(c, name = 'cuda_gs_pack')
147 use, intrinsic :: iso_c_binding
148 implicit none
149 integer(c_int), value :: n, offset
150 type(c_ptr), value :: u_d, buf_d, dof_d, stream
151 end subroutine cuda_gs_pack
152 end interface
153
154 interface
155 subroutine cuda_gs_unpack(u_d, op, buf_d, dof_d, offset, n, stream) &
156 bind(c, name = 'cuda_gs_unpack')
157 use, intrinsic :: iso_c_binding
158 implicit none
159 integer(c_int), value :: op, offset, n
160 type(c_ptr), value :: u_d, buf_d, dof_d, stream
161 end subroutine cuda_gs_unpack
162 end interface
163
164 interface
165 subroutine cuda_gs_pack_vec(u_d, buf_d, dof_d, offset, n, nc, ns, stream) &
166 bind(c, name = 'cuda_gs_pack_vec')
167 use, intrinsic :: iso_c_binding
168 implicit none
169 integer(c_int), value :: offset, n, nc, ns
170 type(c_ptr), value :: u_d, buf_d, dof_d, stream
171 end subroutine cuda_gs_pack_vec
172 end interface
173
174 interface
175 subroutine cuda_gs_unpack_vec(u_d, op, buf_d, dof_d, offset, n, nc, ns, &
176 stream) bind(c, name = 'cuda_gs_unpack_vec')
177 use, intrinsic :: iso_c_binding
178 implicit none
179 integer(c_int), value :: op, offset, n, nc, ns
180 type(c_ptr), value :: u_d, buf_d, dof_d, stream
181 end subroutine cuda_gs_unpack_vec
182 end interface
183#endif
184
185 interface
186 subroutine device_mpi_init_reqs(n, reqs) &
187 bind(c, name = 'device_mpi_init_reqs')
188 use, intrinsic :: iso_c_binding
189 implicit none
190 integer(c_int), value :: n
191 type(c_ptr) :: reqs
192 end subroutine device_mpi_init_reqs
193 end interface
194
195 interface
196 subroutine device_mpi_free_reqs(reqs) &
197 bind(c, name = 'device_mpi_free_reqs')
198 use, intrinsic :: iso_c_binding
199 implicit none
200 type(c_ptr) :: reqs
201 end subroutine device_mpi_free_reqs
202 end interface
203
204 interface
205 subroutine device_mpi_isend(buf_d, offset, nbytes, rank, tag, reqs, i) &
206 bind(c, name = 'device_mpi_isend')
207 use, intrinsic :: iso_c_binding
208 implicit none
209 integer(c_int), value :: offset, nbytes, rank, tag, i
210 type(c_ptr), value :: buf_d, reqs
211 end subroutine device_mpi_isend
212 end interface
213
214 interface
215 subroutine device_mpi_irecv(buf_d, offset, nbytes, rank, tag, reqs, i) &
216 bind(c, name = 'device_mpi_irecv')
217 use, intrinsic :: iso_c_binding
218 implicit none
219 integer(c_int), value :: offset, nbytes, rank, tag, i
220 type(c_ptr), value :: buf_d, reqs
221 end subroutine device_mpi_irecv
222 end interface
223
224 interface
225 subroutine device_mpi_waitall(n, reqs) &
226 bind(c, name = 'device_mpi_waitall')
227 use, intrinsic :: iso_c_binding
228 implicit none
229 integer(c_int), value :: n
230 type(c_ptr), value :: reqs
231 end subroutine device_mpi_waitall
232 end interface
233
234contains
235
238 subroutine gs_device_crystal_init(this, send_pe, recv_pe)
239 class(gs_device_crystal_t), intent(inout) :: this
240 type(stack_i4_t), intent(inout) :: send_pe
241 type(stack_i4_t), intent(inout) :: recv_pe
242 integer, allocatable :: dofs(:)
243 integer :: i, nst
244 integer(c_size_t) :: sz
245 real(c_rp) :: rp_dummy
246
247 call this%init_order(send_pe, recv_pe)
248
249 call this%plan%init(this%send_pe, this%recv_pe, this%send_dof, &
250 this%recv_dof)
251
252 call device_mpi_init_reqs(1, this%sreqs)
253 call device_mpi_init_reqs(2, this%rreqs)
254
255 sz = c_sizeof(rp_dummy) * this%plan%nwrk
256 call device_alloc(this%buf_d(1), sz)
257 call device_alloc(this%buf_d(2), sz)
258 sz = c_sizeof(rp_dummy) * this%plan%nsmax
259 call device_alloc(this%sbuf_d, sz)
260
261 sz = c_sizeof(rp_dummy) * gs_vec_nc * this%plan%nwrk
262 call device_alloc(this%buf_v_d(1), sz)
263 call device_alloc(this%buf_v_d(2), sz)
264 sz = c_sizeof(rp_dummy) * gs_vec_nc * this%plan%nsmax
265 call device_alloc(this%sbuf_v_d, sz)
266
267 ! The first stage gathers straight out of the shared vector
268 if (this%plan%nstage .gt. 0) then
269 call cr_upload(this%pack_keep_d, this%plan%pack_keep_dof, &
270 this%plan%stage(1)%nkw)
271 call cr_upload(this%pack_send_d, this%plan%pack_send_dof, &
272 this%plan%stage(1)%nsw)
273 end if
274
275 ! A shared dof may be delivered by several peers, so the reduction
276 ! marks the repeats for the unpack kernel to handle atomically
277 allocate(dofs(max(this%plan%nfinal, 1)))
278 call cr_mark_dupes(this%plan%unpack_dof, dofs, this%plan%nfinal)
279 call cr_upload(this%unpack_d, dofs, this%plan%nfinal)
280 deallocate(dofs)
281
282 nst = max(this%plan%nstage, 1)
283 allocate(this%keep_idx_d(nst), this%send_idx_d(nst))
284 allocate(this%keep_idx_v_d(nst), this%send_idx_v_d(nst))
285 this%keep_idx_d = c_null_ptr
286 this%send_idx_d = c_null_ptr
287 this%keep_idx_v_d = c_null_ptr
288 this%send_idx_v_d = c_null_ptr
289
290 ! Stage one has no index lists: its words come from the shared vector
291 do i = 2, this%plan%nstage
292 associate(st => this%plan%stage(i))
293 if (.not. st%inplace) then
294 call cr_upload(this%keep_idx_d(i), st%keep_idx, st%nkw)
295 end if
296 if (st%dst .ge. 0) then
297 call cr_upload(this%send_idx_d(i), st%send_idx, st%nsw)
298 end if
299 end associate
300 end do
301
302 this%vec_supported = .true.
303
304 end subroutine gs_device_crystal_init
305
307 subroutine gs_device_crystal_free(this)
308 class(gs_device_crystal_t), intent(inout) :: this
309 integer :: i
310
311 if (c_associated(this%sreqs)) call device_mpi_free_reqs(this%sreqs)
312 if (c_associated(this%rreqs)) call device_mpi_free_reqs(this%rreqs)
313
314 do i = 1, 2
315 if (c_associated(this%buf_d(i))) call device_free(this%buf_d(i))
316 if (c_associated(this%buf_v_d(i))) call device_free(this%buf_v_d(i))
317 end do
318 if (c_associated(this%sbuf_d)) call device_free(this%sbuf_d)
319 if (c_associated(this%sbuf_v_d)) call device_free(this%sbuf_v_d)
320
321 if (c_associated(this%pack_keep_d)) call device_free(this%pack_keep_d)
322 if (c_associated(this%pack_send_d)) call device_free(this%pack_send_d)
323 if (c_associated(this%unpack_d)) call device_free(this%unpack_d)
324
325 call cr_free_ptrs(this%keep_idx_d)
326 call cr_free_ptrs(this%send_idx_d)
327 call cr_free_ptrs(this%keep_idx_v_d)
328 call cr_free_ptrs(this%send_idx_v_d)
329
330 if (allocated(this%keep_idx_d)) deallocate(this%keep_idx_d)
331 if (allocated(this%send_idx_d)) deallocate(this%send_idx_d)
332 if (allocated(this%keep_idx_v_d)) deallocate(this%keep_idx_v_d)
333 if (allocated(this%send_idx_v_d)) deallocate(this%send_idx_v_d)
334
335 this%vec_nc = 0
336
337 call this%plan%free()
338
339 call this%free_order()
340 call this%free_dofs()
341
342 end subroutine gs_device_crystal_free
343
345 subroutine gs_device_crystal_nbrecv(this, tag)
346 class(gs_device_crystal_t), intent(inout) :: this
347 integer, intent(in) :: tag
348
349 this%tag = tag
350 this%nrreq = 0
351 this%nsreq = 0
352 if (this%plan%nstage .eq. 0) return
353
354 associate(st => this%plan%stage(1))
355 if (st%src .ge. 0) then
356 this%nrreq = this%nrreq + 1
357 call device_mpi_irecv(this%buf_d(st%dst_sel), rp*st%nkw, &
358 rp*st%nrw, st%src, tag, this%rreqs, this%nrreq)
359 end if
360 if (st%src2 .ge. 0) then
361 this%nrreq = this%nrreq + 1
362 call device_mpi_irecv(this%buf_d(st%dst_sel), &
363 rp*(st%nkw + st%nrw), rp*st%nr2w, st%src2, tag, &
364 this%rreqs, this%nrreq)
365 end if
366 end associate
367
368 end subroutine gs_device_crystal_nbrecv
369
371 subroutine gs_device_crystal_nbsend(this, u, n, tag, deps, strm)
372 class(gs_device_crystal_t), intent(inout) :: this
373 integer, intent(in) :: n
374 real(kind=rp), dimension(n), intent(inout) :: u
375 integer, intent(in) :: tag
376 type(c_ptr), intent(inout) :: deps
377 type(c_ptr), intent(inout) :: strm
378 type(c_ptr) :: u_d
379
380 if (this%plan%nstage .eq. 0) return
381
382 u_d = device_get_ptr(u)
383
384 associate(st => this%plan%stage(1))
385 if (st%dst .ge. 0) then
386 call cr_gather(u_d, this%sbuf_d, this%pack_send_d, st%nsw, strm)
387 call device_sync(strm)
388 this%nsreq = 1
389 call device_mpi_isend(this%sbuf_d, 0, rp*st%nsw, st%dst, tag, &
390 this%sreqs, 1)
391 end if
392
393 ! The words that stay put, into the column the arriving ones are
394 ! being received behind
395 call cr_gather(u_d, this%buf_d(st%dst_sel), this%pack_keep_d, &
396 st%nkw, strm)
397 end associate
398
399 end subroutine gs_device_crystal_nbsend
400
403 subroutine gs_device_crystal_nbwait(this, u, n, op, strm)
404 class(gs_device_crystal_t), intent(inout) :: this
405 integer, intent(in) :: n
406 real(kind=rp), dimension(n), intent(inout) :: u
407 type(c_ptr), intent(inout) :: strm
408 integer :: op
409 integer :: s
410 type(c_ptr) :: u_d
411
412 if (this%plan%nstage .eq. 0) return
413
414 u_d = device_get_ptr(u)
415
416 call device_mpi_waitall(this%nrreq, this%rreqs)
417 call device_mpi_waitall(this%nsreq, this%sreqs)
418
419 do s = 2, this%plan%nstage
420 associate(st => this%plan%stage(s))
421 this%nrreq = 0
422 this%nsreq = 0
423 if (st%src .ge. 0) then
424 this%nrreq = this%nrreq + 1
425 call device_mpi_irecv(this%buf_d(st%dst_sel), rp*st%nkw, &
426 rp*st%nrw, st%src, this%tag, this%rreqs, this%nrreq)
427 end if
428 if (st%src2 .ge. 0) then
429 this%nrreq = this%nrreq + 1
430 call device_mpi_irecv(this%buf_d(st%dst_sel), &
431 rp*(st%nkw + st%nrw), rp*st%nr2w, st%src2, this%tag, &
432 this%rreqs, this%nrreq)
433 end if
434
435 if (st%dst .ge. 0) then
436 call cr_gather(this%buf_d(st%src_sel), this%sbuf_d, &
437 this%send_idx_d(s), st%nsw, strm)
438 call device_sync(strm)
439 this%nsreq = 1
440 call device_mpi_isend(this%sbuf_d, 0, rp*st%nsw, st%dst, &
441 this%tag, this%sreqs, 1)
442 end if
443
444 ! A stage that sends nothing keeps every word where it is, and is
445 ! receiving into the column it already occupies
446 if (.not. st%inplace) then
447 call cr_gather(this%buf_d(st%src_sel), this%buf_d(st%dst_sel), &
448 this%keep_idx_d(s), st%nkw, strm)
449 end if
450
451 call device_mpi_waitall(this%nrreq, this%rreqs)
452 call device_mpi_waitall(this%nsreq, this%sreqs)
453 call device_sync(strm)
454 end associate
455 end do
456
457 call cr_scatter(u_d, op, this%buf_d(this%plan%final_sel), &
458 this%unpack_d, this%plan%nfinal, strm)
459
460 call device_sync(strm)
461
462 end subroutine gs_device_crystal_nbwait
463
465 subroutine gs_device_crystal_nbrecv_vec(this, tag, nc)
466 class(gs_device_crystal_t), intent(inout) :: this
467 integer, intent(in) :: tag, nc
468
469 if (nc .gt. gs_vec_nc) then
470 call neko_error('gs_device_crystal: too many components in ' // &
471 'vector exchange')
472 end if
473
474 call cr_vec_index(this, nc)
475
476 this%tag = tag
477 this%nrreq = 0
478 this%nsreq = 0
479 if (this%plan%nstage .eq. 0) return
480
481 associate(st => this%plan%stage(1))
482 if (st%src .ge. 0) then
483 this%nrreq = this%nrreq + 1
484 call device_mpi_irecv(this%buf_v_d(st%dst_sel), rp*nc*st%nkw, &
485 rp*nc*st%nrw, st%src, tag, this%rreqs, this%nrreq)
486 end if
487 if (st%src2 .ge. 0) then
488 this%nrreq = this%nrreq + 1
489 call device_mpi_irecv(this%buf_v_d(st%dst_sel), &
490 rp*nc*(st%nkw + st%nrw), rp*nc*st%nr2w, st%src2, tag, &
491 this%rreqs, this%nrreq)
492 end if
493 end associate
494
495 end subroutine gs_device_crystal_nbrecv_vec
496
500 subroutine gs_device_crystal_nbsend_vec(this, u, n, nc, tag, deps, strm)
501 class(gs_device_crystal_t), intent(inout) :: this
502 integer, intent(in) :: n, nc
503 real(kind=rp), dimension(nc*n), intent(inout) :: u
504 integer, intent(in) :: tag
505 type(c_ptr), intent(inout) :: deps
506 type(c_ptr), intent(inout) :: strm
507 type(c_ptr) :: u_d
508
509 if (this%plan%nstage .eq. 0) return
510
511 u_d = device_get_ptr(u)
512
513 associate(st => this%plan%stage(1))
514 if (st%dst .ge. 0) then
515 call cr_gather_vec(u_d, this%sbuf_v_d, this%pack_send_d, st%nsw, &
516 nc, n, strm)
517 call device_sync(strm)
518 this%nsreq = 1
519 call device_mpi_isend(this%sbuf_v_d, 0, rp*nc*st%nsw, st%dst, &
520 tag, this%sreqs, 1)
521 end if
522
523 call cr_gather_vec(u_d, this%buf_v_d(st%dst_sel), this%pack_keep_d, &
524 st%nkw, nc, n, strm)
525 end associate
526
527 end subroutine gs_device_crystal_nbsend_vec
528
531 subroutine gs_device_crystal_nbwait_vec(this, u, n, nc, op, strm)
532 class(gs_device_crystal_t), intent(inout) :: this
533 integer, intent(in) :: n, nc
534 real(kind=rp), dimension(nc*n), intent(inout) :: u
535 type(c_ptr), intent(inout) :: strm
536 integer :: op
537 integer :: s
538 type(c_ptr) :: u_d
539
540 if (this%plan%nstage .eq. 0) return
541
542 u_d = device_get_ptr(u)
543
544 call device_mpi_waitall(this%nrreq, this%rreqs)
545 call device_mpi_waitall(this%nsreq, this%sreqs)
546
547 do s = 2, this%plan%nstage
548 associate(st => this%plan%stage(s))
549 this%nrreq = 0
550 this%nsreq = 0
551 if (st%src .ge. 0) then
552 this%nrreq = this%nrreq + 1
553 call device_mpi_irecv(this%buf_v_d(st%dst_sel), rp*nc*st%nkw, &
554 rp*nc*st%nrw, st%src, this%tag, this%rreqs, this%nrreq)
555 end if
556 if (st%src2 .ge. 0) then
557 this%nrreq = this%nrreq + 1
558 call device_mpi_irecv(this%buf_v_d(st%dst_sel), &
559 rp*nc*(st%nkw + st%nrw), rp*nc*st%nr2w, st%src2, &
560 this%tag, this%rreqs, this%nrreq)
561 end if
562
563 ! The interleaved layout makes a stage's word movement the same
564 ! indexed gather as in the scalar case, over nc times as many
565 ! words, so the plain pack kernel drives it here too
566 if (st%dst .ge. 0) then
567 call cr_gather(this%buf_v_d(st%src_sel), this%sbuf_v_d, &
568 this%send_idx_v_d(s), nc*st%nsw, strm)
569 call device_sync(strm)
570 this%nsreq = 1
571 call device_mpi_isend(this%sbuf_v_d, 0, rp*nc*st%nsw, st%dst, &
572 this%tag, this%sreqs, 1)
573 end if
574
575 if (.not. st%inplace) then
576 call cr_gather(this%buf_v_d(st%src_sel), &
577 this%buf_v_d(st%dst_sel), this%keep_idx_v_d(s), &
578 nc*st%nkw, strm)
579 end if
580
581 call device_mpi_waitall(this%nrreq, this%rreqs)
582 call device_mpi_waitall(this%nsreq, this%sreqs)
583 call device_sync(strm)
584 end associate
585 end do
586
587 call cr_scatter_vec(u_d, op, this%buf_v_d(this%plan%final_sel), &
588 this%unpack_d, this%plan%nfinal, nc, n, strm)
589
590 call device_sync(strm)
591
592 end subroutine gs_device_crystal_nbwait_vec
593
596 subroutine cr_gather(src_d, dst_d, idx_d, n, strm)
597 type(c_ptr), intent(in) :: src_d, dst_d, idx_d
598 integer, intent(in) :: n
599 type(c_ptr), intent(inout) :: strm
600
601 if (n .le. 0) return
602
603#ifdef HAVE_HIP
604 call hip_gs_pack(src_d, dst_d, idx_d, 0, n, strm)
605#elif HAVE_CUDA
606 call cuda_gs_pack(src_d, dst_d, idx_d, 0, n, strm)
607#else
608 call neko_error('gs_device_crystal: no backend')
609#endif
610
611 end subroutine cr_gather
612
615 subroutine cr_gather_vec(src_d, dst_d, idx_d, n, nc, ns, strm)
616 type(c_ptr), intent(in) :: src_d, dst_d, idx_d
617 integer, intent(in) :: n, nc, ns
618 type(c_ptr), intent(inout) :: strm
619
620 if (n .le. 0) return
621
622#ifdef HAVE_HIP
623 call hip_gs_pack_vec(src_d, dst_d, idx_d, 0, n, nc, ns, strm)
624#elif HAVE_CUDA
625 call cuda_gs_pack_vec(src_d, dst_d, idx_d, 0, n, nc, ns, strm)
626#else
627 call neko_error('gs_device_crystal: no backend')
628#endif
629
630 end subroutine cr_gather_vec
631
633 subroutine cr_scatter(u_d, op, buf_d, idx_d, n, strm)
634 type(c_ptr), intent(in) :: u_d, buf_d, idx_d
635 integer, intent(in) :: op, n
636 type(c_ptr), intent(inout) :: strm
637
638 if (n .le. 0) return
639
640#ifdef HAVE_HIP
641 call hip_gs_unpack(u_d, op, buf_d, idx_d, 0, n, strm)
642#elif HAVE_CUDA
643 call cuda_gs_unpack(u_d, op, buf_d, idx_d, 0, n, strm)
644#else
645 call neko_error('gs_device_crystal: no backend')
646#endif
647
648 end subroutine cr_scatter
649
651 subroutine cr_scatter_vec(u_d, op, buf_d, idx_d, n, nc, ns, strm)
652 type(c_ptr), intent(in) :: u_d, buf_d, idx_d
653 integer, intent(in) :: op, n, nc, ns
654 type(c_ptr), intent(inout) :: strm
655
656 if (n .le. 0) return
657
658#ifdef HAVE_HIP
659 call hip_gs_unpack_vec(u_d, op, buf_d, idx_d, 0, n, nc, ns, strm)
660#elif HAVE_CUDA
661 call cuda_gs_unpack_vec(u_d, op, buf_d, idx_d, 0, n, nc, ns, strm)
662#else
663 call neko_error('gs_device_crystal: no backend')
664#endif
665
666 end subroutine cr_scatter_vec
667
673 subroutine cr_vec_index(this, nc)
674 class(gs_device_crystal_t), intent(inout) :: this
675 integer, intent(in) :: nc
676 integer, allocatable :: idx(:)
677 integer :: i
678
679 if (this%vec_nc .eq. nc) return
680
681 call cr_free_ptrs(this%keep_idx_v_d)
682 call cr_free_ptrs(this%send_idx_v_d)
683
684 do i = 2, this%plan%nstage
685 associate(st => this%plan%stage(i))
686 if (.not. st%inplace) then
687 call cr_expand(st%keep_idx, st%nkw, nc, idx)
688 call cr_upload(this%keep_idx_v_d(i), idx, nc*st%nkw)
689 deallocate(idx)
690 end if
691 if (st%dst .ge. 0) then
692 call cr_expand(st%send_idx, st%nsw, nc, idx)
693 call cr_upload(this%send_idx_v_d(i), idx, nc*st%nsw)
694 deallocate(idx)
695 end if
696 end associate
697 end do
698
699 this%vec_nc = nc
700
701 end subroutine cr_vec_index
702
704 subroutine cr_expand(idx, n, nc, out)
705 integer, intent(in) :: idx(:)
706 integer, intent(in) :: n, nc
707 integer, allocatable, intent(out) :: out(:)
708 integer :: j, c
709
710 allocate(out(max(nc*n, 1)))
711 do j = 1, n
712 do c = 1, nc
713 out(nc*(j-1) + c) = nc*(idx(j) - 1) + c
714 end do
715 end do
716
717 end subroutine cr_expand
718
721 subroutine cr_upload(ptr, idx, n)
722 type(c_ptr), intent(inout) :: ptr
723 integer, intent(inout) :: idx(:)
724 integer, intent(in) :: n
725 integer(c_size_t) :: sz
726 integer(c_int32_t) :: i4_dummy
727
728 if (c_associated(ptr)) call device_free(ptr)
729 ptr = c_null_ptr
730
731 if (n .le. 0) return
732
733 sz = c_sizeof(i4_dummy) * n
734 call device_alloc(ptr, sz)
735 call device_memcpy(idx, ptr, n, host_to_device, sync = .true.)
736
737 end subroutine cr_upload
738
740 subroutine cr_free_ptrs(ptrs)
741 type(c_ptr), allocatable, intent(inout) :: ptrs(:)
742 integer :: i
743
744 if (.not. allocated(ptrs)) return
745
746 do i = 1, size(ptrs)
747 if (c_associated(ptrs(i))) call device_free(ptrs(i))
748 ptrs(i) = c_null_ptr
749 end do
750
751 end subroutine cr_free_ptrs
752
755 subroutine cr_mark_dupes(dof, out, n)
756 integer, intent(in) :: dof(:)
757 integer, intent(out) :: out(:)
758 integer, intent(in) :: n
759 type(htable_i4_t) :: doftable
760 integer :: j, dupe, key, val
761
762 if (n .le. 0) return
763
764 ! The table takes its key and data as intent(inout), so neither the
765 ! index list nor the loop counter can be handed to it directly
766 call doftable%init(2*n)
767 do j = 1, n
768 key = dof(j)
769 if (doftable%get(key, dupe) .eq. 0) then
770 if (out(dupe) .gt. 0) out(dupe) = -out(dupe)
771 out(j) = -dof(j)
772 else
773 key = dof(j)
774 val = j
775 call doftable%set(key, val)
776 out(j) = dof(j)
777 end if
778 end do
779 call doftable%free()
780
781 end subroutine cr_mark_dupes
782
783end 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_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