Skip to content

Commit 1f84a5a

Browse files
committed
release gil
1 parent 279c943 commit 1f84a5a

5 files changed

Lines changed: 104 additions & 72 deletions

File tree

cuda_core/cuda/core/experimental/_device.pyx

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ cdef class DeviceProperties:
5555
cdef inline _get_attribute(self, cydriver.CUdevice_attribute attr):
5656
"""Retrieve the attribute value directly from the driver."""
5757
cdef int val
58-
HANDLE_RETURN(cydriver.cuDeviceGetAttribute(&val, attr, self._handle))
58+
with nogil:
59+
HANDLE_RETURN(cydriver.cuDeviceGetAttribute(&val, attr, self._handle))
5960
return val
6061

6162
cdef _get_cached_attribute(self, attr):
@@ -949,7 +950,8 @@ cdef cydriver.CUcontext _get_primary_context(int dev_id) except?NULL:
949950
primary_ctxs = _tls.primary_ctxs = [0] * total
950951
cdef cydriver.CUcontext ctx = <cydriver.CUcontext><uintptr_t>(primary_ctxs[dev_id])
951952
if ctx == NULL:
952-
HANDLE_RETURN(cydriver.cuDevicePrimaryCtxRetain(&ctx, dev_id))
953+
with nogil:
954+
HANDLE_RETURN(cydriver.cuDevicePrimaryCtxRetain(&ctx, dev_id))
953955
primary_ctxs[dev_id] = <uintptr_t>(ctx)
954956
return ctx
955957

@@ -985,19 +987,21 @@ class Device:
985987
def __new__(cls, device_id: Optional[int] = None):
986988
global _is_cuInit
987989
if _is_cuInit is False:
988-
with _lock:
990+
with _lock, nogil:
989991
HANDLE_RETURN(cydriver.cuInit(0))
990992
_is_cuInit = True
991993

992994
# important: creating a Device instance does not initialize the GPU!
993995
cdef cydriver.CUdevice dev
994996
cdef cydriver.CUcontext ctx
995997
if device_id is None:
996-
err = cydriver.cuCtxGetDevice(&dev)
998+
with nogil:
999+
err = cydriver.cuCtxGetDevice(&dev)
9971000
if err == cydriver.CUresult.CUDA_SUCCESS:
9981001
device_id = int(dev)
9991002
elif err == cydriver.CUresult.CUDA_ERROR_INVALID_CONTEXT:
1000-
HANDLE_RETURN(cydriver.cuCtxGetCurrent(&ctx))
1003+
with nogil:
1004+
HANDLE_RETURN(cydriver.cuCtxGetCurrent(&ctx))
10011005
assert <void*>(ctx) == NULL
10021006
device_id = 0 # cudart behavior
10031007
else:
@@ -1010,18 +1014,20 @@ class Device:
10101014
try:
10111015
devices = _tls.devices
10121016
except AttributeError:
1013-
HANDLE_RETURN(cydriver.cuDeviceGetCount(&total))
1017+
with nogil:
1018+
HANDLE_RETURN(cydriver.cuDeviceGetCount(&total))
10141019
devices = _tls.devices = []
10151020
for dev_id in range(total):
10161021
device = super().__new__(cls)
10171022
device._id = dev_id
10181023
# If the device is in TCC mode, or does not support memory pools for some other reason,
10191024
# use the SynchronousMemoryResource which does not use memory pools.
1020-
HANDLE_RETURN(
1021-
cydriver.cuDeviceGetAttribute(
1022-
&attr, cydriver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED, dev_id
1025+
with nogil:
1026+
HANDLE_RETURN(
1027+
cydriver.cuDeviceGetAttribute(
1028+
&attr, cydriver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED, dev_id
1029+
)
10231030
)
1024-
)
10251031
if attr == 1:
10261032
device._mr = DeviceMemoryResource(dev_id)
10271033
else:
@@ -1042,16 +1048,18 @@ class Device:
10421048
f"Device {self._id} is not yet initialized, perhaps you forgot to call .set_current() first?"
10431049
)
10441050

1045-
def _get_current_context(self, check_consistency=False) -> driver.CUcontext:
1051+
def _get_current_context(self, bint check_consistency=False) -> driver.CUcontext:
10461052
cdef cydriver.CUcontext ctx
1047-
HANDLE_RETURN(cydriver.cuCtxGetCurrent(&ctx))
1048-
if ctx == NULL:
1049-
raise CUDAError("No context is bound to the calling CPU thread.")
10501053
cdef cydriver.CUdevice dev
1051-
if check_consistency:
1052-
HANDLE_RETURN(cydriver.cuCtxGetDevice(&dev))
1053-
if <int>(dev) != self._id:
1054-
raise CUDAError("Internal error (current device is not equal to Device.device_id)")
1054+
cdef cydriver.CUdevice this_dev = self._id
1055+
with nogil:
1056+
HANDLE_RETURN(cydriver.cuCtxGetCurrent(&ctx))
1057+
if ctx == NULL:
1058+
raise CUDAError("No context is bound to the calling CPU thread.")
1059+
if check_consistency:
1060+
HANDLE_RETURN(cydriver.cuCtxGetDevice(&dev))
1061+
if dev != this_dev:
1062+
raise CUDAError("Internal error (current device is not equal to Device.device_id)")
10551063
return driver.CUcontext(<uintptr_t>ctx)
10561064

10571065
@property
@@ -1080,10 +1088,12 @@ class Device:
10801088

10811089
"""
10821090
cdef cydriver.CUuuid uuid
1083-
IF CUDA_CORE_BUILD_MAJOR == "12":
1084-
HANDLE_RETURN(cydriver.cuDeviceGetUuid_v2(&uuid, self._id))
1085-
ELSE: # 13.0+
1086-
HANDLE_RETURN(cydriver.cuDeviceGetUuid(&uuid, self._id))
1091+
cdef cydriver.CUdevice this_dev = self._id
1092+
with nogil:
1093+
IF CUDA_CORE_BUILD_MAJOR == "12":
1094+
HANDLE_RETURN(cydriver.cuDeviceGetUuid_v2(&uuid, this_dev))
1095+
ELSE: # 13.0+
1096+
HANDLE_RETURN(cydriver.cuDeviceGetUuid(&uuid, this_dev))
10871097
cdef bytes uuid_b = cpython.PyBytes_FromStringAndSize(uuid.bytes, sizeof(uuid.bytes))
10881098
cdef str uuid_hex = uuid_b.hex()
10891099
# 8-4-4-4-12
@@ -1095,7 +1105,10 @@ class Device:
10951105
# Use 256 characters to be consistent with CUDA Runtime
10961106
cdef int LENGTH = 256
10971107
cdef bytes name = bytes(LENGTH)
1098-
HANDLE_RETURN(cydriver.cuDeviceGetName(<char*>name, LENGTH, self._id))
1108+
cdef char* name_ptr = name
1109+
cdef cydriver.CUdevice this_dev = self._id
1110+
with nogil:
1111+
HANDLE_RETURN(cydriver.cuDeviceGetName(name_ptr, LENGTH, this_dev))
10991112
name = name.split(b"\0")[0]
11001113
return name.decode()
11011114

@@ -1198,7 +1211,8 @@ class Device:
11981211
>>> # ... do work on device 0 ...
11991212

12001213
"""
1201-
cdef cydriver.CUcontext _ctx
1214+
cdef cydriver.CUcontext prev_ctx
1215+
cdef cydriver.CUcontext curr_ctx
12021216
if ctx is not None:
12031217
# TODO: revisit once Context is cythonized
12041218
assert_type(ctx, Context)
@@ -1207,16 +1221,19 @@ class Device:
12071221
"the provided context was created on the device with"
12081222
f" id={ctx._id}, which is different from the target id={self._id}"
12091223
)
1210-
# _ctx is the previous context
1211-
HANDLE_RETURN(cydriver.cuCtxPopCurrent(&_ctx))
1212-
HANDLE_RETURN(cydriver.cuCtxPushCurrent(<cydriver.CUcontext>(ctx._handle)))
1224+
# prev_ctx is the previous context
1225+
curr_ctx = <cydriver.CUcontext>(ctx._handle)
1226+
with nogil:
1227+
HANDLE_RETURN(cydriver.cuCtxPopCurrent(&prev_ctx))
1228+
HANDLE_RETURN(cydriver.cuCtxPushCurrent(curr_ctx))
12131229
self._has_inited = True
1214-
if _ctx != NULL:
1215-
return Context._from_ctx(<uintptr_t>(_ctx), self._id)
1230+
if prev_ctx != NULL:
1231+
return Context._from_ctx(<uintptr_t>(prev_ctx), self._id)
12161232
else:
12171233
# use primary ctx
1218-
_ctx = _get_primary_context(self._id)
1219-
HANDLE_RETURN(cydriver.cuCtxSetCurrent(_ctx))
1234+
curr_ctx = _get_primary_context(self._id)
1235+
with nogil:
1236+
HANDLE_RETURN(cydriver.cuCtxSetCurrent(curr_ctx))
12201237
self._has_inited = True
12211238

12221239
def create_context(self, options: ContextOptions = None) -> Context:

cuda_core/cuda/core/experimental/_event.pyx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ cdef class Event:
109109
self._busy_waited = True
110110
if opts.support_ipc:
111111
raise NotImplementedError("WIP: https://github.com/NVIDIA/cuda-python/issues/103")
112-
HANDLE_RETURN(cydriver.cuEventCreate(&self._handle, flags))
112+
with nogil:
113+
HANDLE_RETURN(cydriver.cuEventCreate(&self._handle, flags))
113114
self._device_id = device_id
114115
self._ctx_handle = ctx_handle
115116
return self
@@ -118,7 +119,8 @@ cdef class Event:
118119
if is_shutting_down and is_shutting_down():
119120
return
120121
if self._handle != NULL:
121-
HANDLE_RETURN(cydriver.cuEventDestroy(self._handle))
122+
with nogil:
123+
HANDLE_RETURN(cydriver.cuEventDestroy(self._handle))
122124
self._handle = <cydriver.CUevent>(NULL)
123125

124126
cpdef close(self):
@@ -137,7 +139,8 @@ cdef class Event:
137139
def __sub__(self, other: Event):
138140
# return self - other (in milliseconds)
139141
cdef float timing
140-
err = cydriver.cuEventElapsedTime(&timing, other._handle, self._handle)
142+
with nogil:
143+
err = cydriver.cuEventElapsedTime(&timing, other._handle, self._handle)
141144
if err == 0:
142145
return timing
143146
else:
@@ -187,12 +190,14 @@ cdef class Event:
187190
has been completed.
188191
189192
"""
190-
HANDLE_RETURN(cydriver.cuEventSynchronize(self._handle))
193+
with nogil:
194+
HANDLE_RETURN(cydriver.cuEventSynchronize(self._handle))
191195

192196
@property
193197
def is_done(self) -> bool:
194198
"""Return True if all captured works have been completed, otherwise False."""
195-
result = cydriver.cuEventQuery(self._handle)
199+
with nogil:
200+
result = cydriver.cuEventQuery(self._handle)
196201
if result == cydriver.CUresult.CUDA_SUCCESS:
197202
return True
198203
if result == cydriver.CUresult.CUDA_ERROR_NOT_READY:

cuda_core/cuda/core/experimental/_stream.pyx

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,21 @@ cdef class Stream:
177177
priority = opts.priority
178178

179179
flags = cydriver.CUstream_flags.CU_STREAM_NON_BLOCKING if nonblocking else cydriver.CUstream_flags.CU_STREAM_DEFAULT
180+
# TODO: we might want to consider memoizing high/low per CUDA context and avoid this call
180181
cdef int high, low
181-
HANDLE_RETURN(cydriver.cuCtxGetStreamPriorityRange(&high, &low))
182+
with nogil:
183+
HANDLE_RETURN(cydriver.cuCtxGetStreamPriorityRange(&high, &low))
184+
cdef int prio
182185
if priority is not None:
183-
if not (low <= priority <= high):
186+
prio = priority
187+
if not (low <= prio <= high):
184188
raise ValueError(f"{priority=} is out of range {[low, high]}")
185189
else:
186-
priority = high
190+
prio = high
187191

188192
cdef cydriver.CUstream s
189-
HANDLE_RETURN(cydriver.cuStreamCreateWithPriority(&s, flags, priority))
193+
with nogil:
194+
HANDLE_RETURN(cydriver.cuStreamCreateWithPriority(&s, flags, prio))
190195
self._handle = s
191196
self._owner = None
192197
self._nonblocking = nonblocking
@@ -204,7 +209,8 @@ cdef class Stream:
204209

205210
if self._owner is None:
206211
if self._handle and not self._builtin:
207-
HANDLE_RETURN(cydriver.cuStreamDestroy(self._handle))
212+
with nogil:
213+
HANDLE_RETURN(cydriver.cuStreamDestroy(self._handle))
208214
else:
209215
self._owner = None
210216
self._handle = <cydriver.CUstream>(NULL)
@@ -238,7 +244,8 @@ cdef class Stream:
238244
"""Return True if this is a nonblocking stream, otherwise False."""
239245
cdef unsigned int flags
240246
if self._nonblocking is None:
241-
HANDLE_RETURN(cydriver.cuStreamGetFlags(self._handle, &flags))
247+
with nogil:
248+
HANDLE_RETURN(cydriver.cuStreamGetFlags(self._handle, &flags))
242249
if flags & cydriver.CUstream_flags.CU_STREAM_NON_BLOCKING:
243250
self._nonblocking = True
244251
else:
@@ -250,13 +257,15 @@ cdef class Stream:
250257
"""Return the stream priority."""
251258
cdef int prio
252259
if self._priority is None:
253-
HANDLE_RETURN(cydriver.cuStreamGetPriority(self._handle, &prio))
260+
with nogil:
261+
HANDLE_RETURN(cydriver.cuStreamGetPriority(self._handle, &prio))
254262
self._priority = prio
255263
return self._priority
256264

257265
def sync(self):
258266
"""Synchronize the stream."""
259-
HANDLE_RETURN(cydriver.cuStreamSynchronize(self._handle))
267+
with nogil:
268+
HANDLE_RETURN(cydriver.cuStreamSynchronize(self._handle))
260269

261270
def record(self, event: Event = None, options: EventOptions = None) -> Event:
262271
"""Record an event onto the stream.
@@ -299,11 +308,12 @@ cdef class Stream:
299308
"""
300309
cdef cydriver.CUevent event
301310
cdef cydriver.CUstream stream
302-
cdef bint discard_event
303311

304312
if isinstance(event_or_stream, Event):
305313
event = <cydriver.CUevent><uintptr_t>(event_or_stream.handle)
306-
discard_event = False
314+
with nogil:
315+
# TODO: support flags other than 0?
316+
HANDLE_RETURN(cydriver.cuStreamWaitEvent(self._handle, event, 0))
307317
else:
308318
if isinstance(event_or_stream, Stream):
309319
stream = <cydriver.CUstream><uintptr_t>(event_or_stream.handle)
@@ -316,14 +326,12 @@ cdef class Stream:
316326
f" got {type(event_or_stream)}"
317327
) from e
318328
stream = <cydriver.CUstream><uintptr_t>(s.handle)
319-
HANDLE_RETURN(cydriver.cuEventCreate(&event, cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING))
320-
HANDLE_RETURN(cydriver.cuEventRecord(event, stream))
321-
discard_event = True
322-
323-
# TODO: support flags other than 0?
324-
HANDLE_RETURN(cydriver.cuStreamWaitEvent(self._handle, event, 0))
325-
if discard_event:
326-
HANDLE_RETURN(cydriver.cuEventDestroy(event))
329+
with nogil:
330+
HANDLE_RETURN(cydriver.cuEventCreate(&event, cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING))
331+
HANDLE_RETURN(cydriver.cuEventRecord(event, stream))
332+
# TODO: support flags other than 0?
333+
HANDLE_RETURN(cydriver.cuStreamWaitEvent(self._handle, event, 0))
334+
HANDLE_RETURN(cydriver.cuEventDestroy(event))
327335

328336
@property
329337
def device(self) -> Device:
@@ -344,7 +352,8 @@ cdef class Stream:
344352
# TODO: consider making self._ctx_handle typed?
345353
cdef cydriver.CUcontext ctx
346354
if self._ctx_handle is None:
347-
HANDLE_RETURN(cydriver.cuStreamGetCtx(self._handle, &ctx))
355+
with nogil:
356+
HANDLE_RETURN(cydriver.cuStreamGetCtx(self._handle, &ctx))
348357
self._ctx_handle = driver.CUcontext(<uintptr_t>ctx)
349358
return 0
350359

cuda_core/cuda/core/experimental/_utils/cuda_utils.pxd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ ctypedef fused supported_error_type:
1212
cydriver.CUresult
1313

1414

15-
cdef int HANDLE_RETURN(supported_error_type err) except?-1
15+
cdef int HANDLE_RETURN(supported_error_type err) except?-1 nogil
1616

1717

1818
# TODO: stop exposing these within the codebase?
19-
cpdef int _check_driver_error(error) except?-1
19+
cpdef int _check_driver_error(cydriver.CUresult error) except?-1 nogil
2020
cpdef int _check_runtime_error(error) except?-1
2121
cpdef int _check_nvrtc_error(error) except?-1
2222

cuda_core/cuda/core/experimental/_utils/cuda_utils.pyx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,32 +52,33 @@ def _reduce_3_tuple(t: tuple):
5252
return t[0] * t[1] * t[2]
5353

5454

55-
cdef int HANDLE_RETURN(supported_error_type err) except?-1:
55+
cdef int HANDLE_RETURN(supported_error_type err) except?-1 nogil:
5656
if supported_error_type is cydriver.CUresult:
5757
if err != cydriver.CUresult.CUDA_SUCCESS:
5858
return _check_driver_error(err)
5959

6060

61-
cdef object _DRIVER_SUCCESS = driver.CUresult.CUDA_SUCCESS
6261
cdef object _RUNTIME_SUCCESS = runtime.cudaError_t.cudaSuccess
6362
cdef object _NVRTC_SUCCESS = nvrtc.nvrtcResult.NVRTC_SUCCESS
6463

6564

66-
cpdef inline int _check_driver_error(error) except?-1:
67-
if error == _DRIVER_SUCCESS:
65+
cpdef inline int _check_driver_error(cydriver.CUresult error) except?-1 nogil:
66+
if error == cydriver.CUresult.CUDA_SUCCESS:
6867
return 0
69-
name_err, name = driver.cuGetErrorName(error)
70-
if name_err != _DRIVER_SUCCESS:
68+
cdef const char* name
69+
name_err = cydriver.cuGetErrorName(error, &name)
70+
if name_err != cydriver.CUresult.CUDA_SUCCESS:
7171
raise CUDAError(f"UNEXPECTED ERROR CODE: {error}")
72-
name = name.decode()
73-
expl = DRIVER_CU_RESULT_EXPLANATIONS.get(int(error))
74-
if expl is not None:
75-
raise CUDAError(f"{name}: {expl}")
76-
desc_err, desc = driver.cuGetErrorString(error)
77-
if desc_err != _DRIVER_SUCCESS:
78-
raise CUDAError(f"{name}")
79-
desc = desc.decode()
80-
raise CUDAError(f"{name}: {desc}")
72+
with gil:
73+
# TODO: consider lower this to Cython
74+
expl = DRIVER_CU_RESULT_EXPLANATIONS.get(int(error))
75+
if expl is not None:
76+
raise CUDAError(f"{name.decode()}: {expl}")
77+
cdef const char* desc
78+
desc_err = cydriver.cuGetErrorString(error, &desc)
79+
if desc_err != cydriver.CUresult.CUDA_SUCCESS:
80+
raise CUDAError(f"{name.decode()}")
81+
raise CUDAError(f"{name.decode()}: {desc.decode()}")
8182

8283

8384
cpdef inline int _check_runtime_error(error) except?-1:

0 commit comments

Comments
 (0)