Neko 1.99.6
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
cuda_intf.F90
Go to the documentation of this file.
1! Copyright (c) 2021-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!
35 use utils, only : neko_error
36 use, intrinsic :: iso_c_binding
37 !$ use omp_lib
38 implicit none
39
40#ifdef HAVE_CUDA
41
43 enum, bind(c)
44 enumerator :: cudasuccess = 0
45 enumerator :: cudaerrorinvalidvalue = 1
46 enumerator :: cudaerrormemoryallocation = 2
48 end enum
49
51 enum, bind(c)
52 enumerator :: cudamemcpyhosttohost = 0
53 enumerator :: cudamemcpyhosttodevice = 1
54 enumerator :: cudamemcpydevicetohost = 2
55 enumerator :: cudamemcpydevicetodevice = 3
56 enumerator :: cudamemcpydefault = 4
57 end enum
58
59 interface
60 integer(c_int) function cudamalloc(ptr_d, s) &
61 bind(c, name = 'cudaMalloc')
62 use, intrinsic :: iso_c_binding
63 implicit none
64 type(c_ptr) :: ptr_d
65 integer(c_size_t), value :: s
66 end function cudamalloc
67
68 integer(c_int) function cudafree(ptr_d) &
69 bind(c, name = 'cudaFree')
70 use, intrinsic :: iso_c_binding
71 implicit none
72 type(c_ptr), value :: ptr_d
73 end function cudafree
74
75 integer(c_int) function cudamemcpy(ptr_dst, ptr_src, s, dir) &
76 bind(c, name = 'cudaMemcpy')
77 use, intrinsic :: iso_c_binding
78 implicit none
79 type(c_ptr), value :: ptr_dst, ptr_src
80 integer(c_size_t), value :: s
81 integer(c_int), value :: dir
82 end function cudamemcpy
83
84 integer(c_int) function cudamemcpyasync(ptr_dst, ptr_src, s, dir, stream) &
85 bind(c, name = 'cudaMemcpyAsync')
86 use, intrinsic :: iso_c_binding
87 implicit none
88 type(c_ptr), value :: ptr_dst, ptr_src, stream
89 integer(c_size_t), value :: s
90 integer(c_int), value :: dir
91 end function cudamemcpyasync
92
93 integer(c_int) function cudamemsetasync(ptr, v, s, stream) &
94 bind(c, name = 'cudaMemsetAsync')
95 use, intrinsic :: iso_c_binding
96 implicit none
97 type(c_ptr), value :: ptr, stream
98 integer(c_int), value :: v
99 integer(c_size_t), value :: s
100 end function cudamemsetasync
101
102 integer(c_int) function cudadevicesynchronize() &
103 bind(c, name = 'cudaDeviceSynchronize')
104 use, intrinsic :: iso_c_binding
105 implicit none
106 end function cudadevicesynchronize
107
108 integer(c_int) function cudadevicereset() &
109 bind(c, name = 'cudaDeviceReset')
110 use, intrinsic :: iso_c_binding
111 implicit none
112 end function cudadevicereset
113
114 subroutine cuda_buffer_free_all() &
115 bind(c, name = 'cuda_buffer_free_all')
116 end subroutine cuda_buffer_free_all
117
118 integer(c_int) function cudagetdeviceproperties(prop, device) &
119 bind(c, name = 'cudaGetDeviceProperties')
120 use, intrinsic :: iso_c_binding
121 implicit none
122 type(c_ptr), value :: prop
123 integer(c_int), value :: device
124 end function cudagetdeviceproperties
125
126 integer(c_int) function cudastreamcreate(stream) &
127 bind(c, name = 'cudaStreamCreate')
128 use, intrinsic :: iso_c_binding
129 implicit none
130 type(c_ptr) :: stream
131 end function cudastreamcreate
132
133 integer(c_int) function cudastreamcreatewithflags(stream, flags) &
134 bind(c, name = 'cudaStreamCreateWithFlags')
135 use, intrinsic :: iso_c_binding
136 implicit none
137 type(c_ptr) :: stream
138 integer(c_int), value :: flags
139 end function cudastreamcreatewithflags
140
141 integer(c_int) function cudastreamcreatewithpriority(stream, flags, prio) &
142 bind(c, name = 'cudaStreamCreateWithPriority')
143 use, intrinsic :: iso_c_binding
144 implicit none
145 type(c_ptr) :: stream
146 integer(c_int), value :: flags, prio
148
149 integer(c_int) function cudastreamdestroy(steam) &
150 bind(c, name = 'cudaStreamDestroy')
151 use, intrinsic :: iso_c_binding
152 implicit none
153 type(c_ptr), value :: steam
154 end function cudastreamdestroy
155
156 integer(c_int) function cudastreamsynchronize(stream) &
157 bind(c, name = 'cudaStreamSynchronize')
158 use, intrinsic :: iso_c_binding
159 implicit none
160 type(c_ptr), value :: stream
161 end function cudastreamsynchronize
162
163 integer(c_int) function cudastreamwaitevent(stream, event, flags) &
164 bind(c, name = 'cudaStreamWaitEvent')
165 use, intrinsic :: iso_c_binding
166 implicit none
167 type(c_ptr), value :: stream, event
168 integer(c_int), value :: flags
169 end function cudastreamwaitevent
170
171 integer(c_int) function cudadevicegetstreampriorityrange &
172 (low_prio, high_prio) &
173 bind(c, name = 'cudaDeviceGetStreamPriorityRange')
174 use, intrinsic :: iso_c_binding
175 implicit none
176 integer(c_int) :: low_prio, high_prio
178
179 integer(c_int) function cudaprofilerstart() &
180 bind(c, name = 'cudaProfilerStart')
181 use, intrinsic :: iso_c_binding
182 implicit none
183 end function cudaprofilerstart
184
185 integer(c_int) function cudaprofilerstop() &
186 bind(c, name = 'cudaProfilerStop')
187 use, intrinsic :: iso_c_binding
188 implicit none
189 end function cudaprofilerstop
190
191 integer(c_int) function cudaeventcreate(event) &
192 bind(c, name = 'cudaEventCreate')
193 use, intrinsic :: iso_c_binding
194 implicit none
195 type(c_ptr) :: event
196 end function cudaeventcreate
197
198 integer(c_int) function cudaeventdestroy(event) &
199 bind(c, name = 'cudaEventDestroy')
200 use, intrinsic :: iso_c_binding
201 implicit none
202 type(c_ptr), value :: event
203 end function cudaeventdestroy
204
205 integer(c_int) function cudaeventcreatewithflags(event, flags) &
206 bind(c, name = 'cudaEventCreateWithFlags')
207 use, intrinsic :: iso_c_binding
208 implicit none
209 type(c_ptr) :: event
210 integer(c_int), value :: flags
211 end function cudaeventcreatewithflags
212
213 integer(c_int) function cudaeventrecord(event, stream) &
214 bind(c, name = 'cudaEventRecord')
215 use, intrinsic :: iso_c_binding
216 implicit none
217 type(c_ptr), value :: event, stream
218 end function cudaeventrecord
219
220 integer(c_int) function cudaeventsynchronize(event) &
221 bind(c, name = 'cudaEventSynchronize')
222 use, intrinsic :: iso_c_binding
223 implicit none
224 type(c_ptr), value :: event
225 end function cudaeventsynchronize
226
227 integer(c_int) function cudagetdevicecount(device_count) &
228 bind(c, name = 'cudaGetDeviceCount')
229 use, intrinsic :: iso_c_binding
230 implicit none
231 integer(c_int) :: device_count
232 end function cudagetdevicecount
233
234 integer(c_int) function cudagetdevice(device) &
235 bind(c, name = 'cudaGetDevice')
236 use, intrinsic :: iso_c_binding
237 implicit none
238 integer(c_int) :: device
239 end function cudagetdevice
240
241 integer(c_int) function cudasetdevice(device) &
242 bind(c, name = 'cudaSetDevice')
243 use, intrinsic :: iso_c_binding
244 implicit none
245 integer(c_int), value :: device
246 end function cudasetdevice
247 end interface
248
249contains
250
251 subroutine cuda_init(glb_cmd_queue, aux_cmd_queue, &
252 STRM_HIGH_PRIO, STRM_LOW_PRIO)
253 type(c_ptr), intent(inout) :: glb_cmd_queue
254 type(c_ptr), intent(inout) :: aux_cmd_queue
255 integer, intent(inout) :: STRM_HIGH_PRIO
256 integer, intent(inout) :: STRM_LOW_PRIO
257 integer(c_int) :: device_id
258 integer :: nthrds = 1
259
260 !$omp parallel
261 !$omp master
262 !$ nthrds = omp_get_num_threads()
263 !$omp end master
264 !$omp end parallel
265
266 ! Ensure that all threads are assigned to the same device
267 if (nthrds .gt. 1) then
268 if (cudagetdevice(device_id) .ne. cudasuccess) then
269 call neko_error('Error retrieving device id')
270 end if
271
272 !$omp parallel
273 if (cudasetdevice(device_id) .ne. cudasuccess) then
274 call neko_error('Error setting device id')
275 end if
276 !$omp end parallel
277 end if
278
279 if (cudadevicegetstreampriorityrange(strm_low_prio, strm_high_prio) &
280 .ne. cudasuccess) then
281 call neko_error('Error retrieving stream priority range')
282 end if
283
284 if (cudastreamcreatewithpriority(glb_cmd_queue, 1, strm_high_prio) &
285 .ne. cudasuccess) then
286 call neko_error('Error creating main stream')
287 end if
288
289 if (cudastreamcreatewithpriority(aux_cmd_queue, 1, strm_low_prio) &
290 .ne. cudasuccess) then
291 call neko_error('Error creating aux stream')
292 end if
293 end subroutine cuda_init
294
295 subroutine cuda_finalize(glb_cmd_queue, aux_cmd_queue)
296 type(c_ptr), intent(inout) :: glb_cmd_queue
297 type(c_ptr), intent(inout) :: aux_cmd_queue
298 integer :: ierr
299
300 ! Release all device buffers held by the device layer
302
303 if (cudastreamdestroy(glb_cmd_queue) .ne. cudasuccess) then
304 call neko_error('Error destroying main stream')
305 end if
306
307 if (cudastreamdestroy(aux_cmd_queue) .ne. cudasuccess) then
308 call neko_error('Error destroying aux stream')
309 end if
310
311 ierr = cudadevicesynchronize()
312
313 ! Best-effort context teardown to release runtime-owned allocations.
314 ! Skipped in pFUnit-enabled builds: unit tests cycle device
315 ! init/finalize with MPI still up, and device-aware communication
316 ! backends (CUDA-aware MPI, NCCL, NVSHMEM) cache the primary
317 ! context from first use; destroying it here would leave them with
318 ! a dangling context
319#ifndef HAVE_PFUNIT
320 ierr = cudadevicereset()
321#endif
322 end subroutine cuda_finalize
323
324 subroutine cuda_device_name(name)
325 character(len=*), intent(inout) :: name
326 character(kind=c_char, len=8192), target :: prop
327 integer :: end_pos
328
329 !
330 ! Yes this is an ugly hack!
331 ! Since we're only interested in the device name (first 256 bytes)
332 ! we pass down a large enough chunk of memory to the cuda runtime
333 ! and extract what we need later on
334 !
335 ! This will of course break if sizeof(cudaDeviceProp) > 8192
336 !
337
338 if (cudagetdeviceproperties(c_loc(prop), 0) .ne. cudasuccess) then
339 call neko_error('Failed to query device')
340 end if
341
342 end_pos = scan(prop(1:256), c_null_char)
343 if (end_pos .ge. 2) then
344 name(1:end_pos-1) = prop(1:end_pos-1)
345 end if
346 end subroutine cuda_device_name
347
349 integer function cuda_device_count()
350 integer(c_int) :: num_devices
351
352 if (cudagetdevicecount(num_devices) .ne. cudasuccess) then
353 call neko_error('Error retrieving device count')
354 end if
355
356 cuda_device_count = num_devices
357 end function cuda_device_count
358
359#endif
360
361end module cuda_intf
Fortran CUDA interface.
Definition cuda_intf.F90:34
subroutine cuda_device_name(name)
subroutine cuda_finalize(glb_cmd_queue, aux_cmd_queue)
@ cudamemcpydevicetohost
Definition cuda_intf.F90:54
@ cudamemcpydefault
Definition cuda_intf.F90:56
@ cudamemcpyhosttohost
Definition cuda_intf.F90:52
@ cudamemcpydevicetodevice
Definition cuda_intf.F90:55
@ cudamemcpyhosttodevice
Definition cuda_intf.F90:53
integer function cuda_device_count()
Return the number of avaialble CUDA devices.
@ cudaerrorinitializationerror
Definition cuda_intf.F90:47
@ cudaerrormemoryallocation
Definition cuda_intf.F90:46
@ cudaerrorinvalidvalue
Definition cuda_intf.F90:45
subroutine cuda_init(glb_cmd_queue, aux_cmd_queue, strm_high_prio, strm_low_prio)
Device abstraction, common interface for various accelerators.
Definition device.F90:34
Utilities.
Definition utils.f90:35