Neko 1.99.9
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
236 integer(c_int) function cuda_device_mp_count() &
237 bind(c, name = 'cuda_device_mp_count')
238 use, intrinsic :: iso_c_binding
239 implicit none
240 end function cuda_device_mp_count
241
244 integer(c_size_t) function cuda_device_total_mem() &
245 bind(c, name = 'cuda_device_total_mem')
246 use, intrinsic :: iso_c_binding
247 implicit none
248 end function cuda_device_total_mem
249
250 integer(c_int) function cudagetdevice(device) &
251 bind(c, name = 'cudaGetDevice')
252 use, intrinsic :: iso_c_binding
253 implicit none
254 integer(c_int) :: device
255 end function cudagetdevice
256
257 integer(c_int) function cudasetdevice(device) &
258 bind(c, name = 'cudaSetDevice')
259 use, intrinsic :: iso_c_binding
260 implicit none
261 integer(c_int), value :: device
262 end function cudasetdevice
263 end interface
264
265contains
266
267 subroutine cuda_init(glb_cmd_queue, aux_cmd_queue, &
268 STRM_HIGH_PRIO, STRM_LOW_PRIO)
269 type(c_ptr), intent(inout) :: glb_cmd_queue
270 type(c_ptr), intent(inout) :: aux_cmd_queue
271 integer, intent(inout) :: STRM_HIGH_PRIO
272 integer, intent(inout) :: STRM_LOW_PRIO
273 integer(c_int) :: device_id
274 integer :: nthrds = 1
275
276 !$omp parallel
277 !$omp master
278 !$ nthrds = omp_get_num_threads()
279 !$omp end master
280 !$omp end parallel
281
282 ! Ensure that all threads are assigned to the same device
283 if (nthrds .gt. 1) then
284 if (cudagetdevice(device_id) .ne. cudasuccess) then
285 call neko_error('Error retrieving device id')
286 end if
287
288 !$omp parallel
289 if (cudasetdevice(device_id) .ne. cudasuccess) then
290 call neko_error('Error setting device id')
291 end if
292 !$omp end parallel
293 end if
294
295 if (cudadevicegetstreampriorityrange(strm_low_prio, strm_high_prio) &
296 .ne. cudasuccess) then
297 call neko_error('Error retrieving stream priority range')
298 end if
299
300 if (cudastreamcreatewithpriority(glb_cmd_queue, 1, strm_high_prio) &
301 .ne. cudasuccess) then
302 call neko_error('Error creating main stream')
303 end if
304
305 if (cudastreamcreatewithpriority(aux_cmd_queue, 1, strm_low_prio) &
306 .ne. cudasuccess) then
307 call neko_error('Error creating aux stream')
308 end if
309 end subroutine cuda_init
310
311 subroutine cuda_finalize(glb_cmd_queue, aux_cmd_queue)
312 type(c_ptr), intent(inout) :: glb_cmd_queue
313 type(c_ptr), intent(inout) :: aux_cmd_queue
314 integer :: ierr
315
316 ! Release all device buffers held by the device layer
318
319 if (cudastreamdestroy(glb_cmd_queue) .ne. cudasuccess) then
320 call neko_error('Error destroying main stream')
321 end if
322
323 if (cudastreamdestroy(aux_cmd_queue) .ne. cudasuccess) then
324 call neko_error('Error destroying aux stream')
325 end if
326
327 ierr = cudadevicesynchronize()
328
329 ! Best-effort context teardown to release runtime-owned allocations.
330 ! Skipped in pFUnit-enabled builds: unit tests cycle device
331 ! init/finalize with MPI still up, and device-aware communication
332 ! backends (CUDA-aware MPI, NCCL, NVSHMEM) cache the primary
333 ! context from first use; destroying it here would leave them with
334 ! a dangling context
335#ifndef HAVE_PFUNIT
336 ierr = cudadevicereset()
337#endif
338 end subroutine cuda_finalize
339
340 subroutine cuda_device_name(name)
341 character(len=*), intent(inout) :: name
342 character(kind=c_char, len=8192), target :: prop
343 integer :: end_pos
344
345 !
346 ! Yes this is an ugly hack!
347 ! Since we're only interested in the device name (first 256 bytes)
348 ! we pass down a large enough chunk of memory to the cuda runtime
349 ! and extract what we need later on
350 !
351 ! This will of course break if sizeof(cudaDeviceProp) > 8192
352 !
353
354 if (cudagetdeviceproperties(c_loc(prop), 0) .ne. cudasuccess) then
355 call neko_error('Failed to query device')
356 end if
357
358 end_pos = scan(prop(1:256), c_null_char)
359 if (end_pos .ge. 2) then
360 name(1:end_pos-1) = prop(1:end_pos-1)
361 end if
362 end subroutine cuda_device_name
363
365 integer function cuda_device_count()
366 integer(c_int) :: num_devices
367
368 if (cudagetdevicecount(num_devices) .ne. cudasuccess) then
369 call neko_error('Error retrieving device count')
370 end if
371
372 cuda_device_count = num_devices
373 end function cuda_device_count
374
375#endif
376
377end module cuda_intf
Number of streaming multiprocessors of the current device (0 if unknown), see device/cuda/props....
Total memory of the current device in bytes (0 if unknown), see device/cuda/props....
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