Coverage for cuda/core/_device_resources.pyx: 65.41%
292 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 01:23 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 01:23 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
5from __future__ import annotations
7from collections.abc import Sequence as SequenceABC
8from dataclasses import dataclass
9from typing import TYPE_CHECKING
11if TYPE_CHECKING:
12 from cuda.core._device import Device
13 from cuda.core.typing import WorkqueueSharingScopeType
15from libc.stdint cimport intptr_t
16from libc.stdlib cimport free, malloc
17from libc.string cimport memset
19from cuda.bindings cimport cydriver
20from cuda.core._rt cimport ContextHandle, GreenCtxHandle, as_cu, get_context_green_ctx
21from cuda.core._utils.cuda_utils cimport check_or_create_options, HANDLE_RETURN
22from cuda.core._utils.cuda_utils import is_sequence
23from cuda.core._utils.version cimport cy_driver_version
24from cuda.core._utils.validators import check_str_enum
27__all__ = [
28 "DeviceResources",
29 "SMResource",
30 "SMResourceOptions",
31 "WorkqueueResource",
32 "WorkqueueResourceOptions",
33]
36# Module-level cached version checks (trinary: 0=unchecked, 1=supported, -1=unsupported)
37cdef int _green_ctx_checked = 0
38cdef int _workqueue_checked = 0
39cdef str _green_ctx_err_msg = ""
40cdef str _workqueue_err_msg = ""
43cdef inline int _check_green_ctx_support() except?-1:
44 global _green_ctx_checked, _green_ctx_err_msg
45 if _green_ctx_checked == 1: 1bFEdDGB2yefqHnAghijklmaICJKLtMsNoOuPpQrRSTxUVzWXY#3$4)56718%9!vZcw0
46 return 0 1bFEdDGB2yefqHnAghijklmaICJKLtMsNoOuPpQrRSTxUVzWXY#3$4)56718%9!vZcw0
47 if _green_ctx_checked == -1: 1aC
48 raise RuntimeError(_green_ctx_err_msg)
49 cdef tuple drv = cy_driver_version() 1aC
50 if drv < (12, 4, 0): 1aC
51 _green_ctx_err_msg = (
52 "Green context support requires CUDA driver 12.4 or newer "
53 f"(current driver: {'.'.join(map(str, drv))})"
54 )
55 _green_ctx_checked = -1
56 raise RuntimeError(_green_ctx_err_msg)
57 _green_ctx_checked = 1 1aC
58 return 0 1aC
61cdef inline int _check_workqueue_support() except?-1:
62 global _workqueue_checked, _workqueue_err_msg
63 if _workqueue_checked == 1: 1B2ynA#3$4)56718%9!
64 return 0 1B2ynA#3$4)56718%9!
65 if _workqueue_checked == -1: 1y
66 raise RuntimeError(_workqueue_err_msg)
67 cdef tuple drv = cy_driver_version() 1y
68 if drv < (13, 1, 0): 1y
69 _workqueue_err_msg = (
70 "WorkqueueResource requires CUDA driver 13.1 or newer "
71 f"(current driver: {'.'.join(map(str, drv))})"
72 )
73 _workqueue_checked = -1
74 raise RuntimeError(_workqueue_err_msg)
75 _workqueue_checked = 1 1y
76 return 0 1y
79@dataclass
80cdef class SMResourceOptions:
81 """Customizable :obj:`SMResource.split` options.
83 Each field accepts a scalar (for a single group) or a ``Sequence``
84 (for multiple groups). ``count`` drives the number of groups; other
85 ``Sequence`` fields must match its length.
87 Attributes
88 ----------
89 count : int or Sequence[int], optional
90 Requested SM count per group. ``None`` means discovery mode
91 (auto-detect). (Default to ``None``)
92 coscheduled_sm_count : int or Sequence[int], optional
93 Minimum number of SMs guaranteed to be co-scheduled in each
94 group. (Default to ``None``)
95 preferred_coscheduled_sm_count : int or Sequence[int], optional
96 Preferred co-scheduled SM count; the driver tries to satisfy
97 this but may fall back to ``coscheduled_sm_count``.
98 (Default to ``None``)
99 backfill : bool or Sequence[bool], optional
100 If ``True``, allow the driver to relax the co-scheduling
101 constraint when assigning SMs. This enables requesting
102 arbitrary aligned SM counts that the driver would otherwise
103 reject due to hardware topology constraints.
104 (Default to ``False``)
105 """
107 count: int | SequenceABC[int] | None = None
108 coscheduled_sm_count: int | SequenceABC[int] | None = None
109 preferred_coscheduled_sm_count: int | SequenceABC[int] | None = None
110 backfill: bool | SequenceABC[bool] = False
113@dataclass
114cdef class WorkqueueResourceOptions:
115 """Customizable :obj:`WorkqueueResource.configure` options.
117 Attributes
118 ----------
119 sharing_scope : :class:`~cuda.core.typing.WorkqueueSharingScopeType` | str, optional
120 Workqueue sharing scope. Accepted values: ``"device_ctx"`` or
121 ``"green_ctx_balanced"``.
122 concurrency_limit : int, optional
123 Expected maximum number of concurrent stream-ordered
124 workloads. Must be ``>= 1`` when set. The effective
125 driver-side cap is ``CUDA_DEVICE_MAX_CONNECTIONS``
126 (typically ``[1, 32]``); configurations may exceed
127 this cap, but the driver will not guarantee that work
128 submission remains non-overlapping. (Default to ``None``)
129 """
131 sharing_scope: WorkqueueSharingScopeType | str | None = None
132 concurrency_limit: int | None = None
134 def __post_init__(self):
135 from cuda.core.typing import WorkqueueSharingScopeType 1n./#$)61%=
136 check_str_enum(self.sharing_scope, WorkqueueSharingScopeType, allow_none=True) 1n./#$)61%=
137 if self.concurrency_limit is not None and self.concurrency_limit < 1: 1n./#$)61%
138 raise ValueError( 1./
139 f"concurrency_limit must be >= 1, got {self.concurrency_limit}" 1./
140 )
143cdef inline int _validate_split_field_length(
144 object value, str field_name, int n_groups, bint count_is_scalar
145) except?-1:
146 if count_is_scalar: 1bdefqnghijklmatsouprxz('vcw
147 if is_sequence(value): 1defnghijklmatsouxz(vcw
148 raise ValueError( 1(
149 f"{field_name} is a Sequence but count is scalar; " 1(
150 "count must be a Sequence to specify multiple groups"
151 )
152 elif is_sequence(value) and len(value) != n_groups: 1bqpr'
153 raise ValueError( 1'
154 f"{field_name} has length {len(value)}, expected {n_groups} " 1'
155 "(must match count)"
156 )
157 return 0 1bdefqnghijklmatsouprxzvcw
160cdef inline int _resolve_group_count(SMResourceOptions options) except?-1:
161 cdef object count = options.count 1bdefqnghijklmatsoupr,-x*z('vcw
162 cdef int n_groups
163 cdef bint count_is_scalar
165 if count is None or isinstance(count, int): 1bdefqnghijklmatsoupr,-x*z('vcw
166 n_groups = 1 1defnghijklmatsouxz(vcw
167 count_is_scalar = True 1defnghijklmatsouxz(vcw
168 elif is_sequence(count): 1bqpr,-*'
169 n_groups = <int>len(count) 1bqpr*'
170 if n_groups == 0: 1bqpr*'
171 raise ValueError("count sequence must not be empty") 1*
172 count_is_scalar = False 1bqpr'
173 else:
174 raise TypeError(f"count must be int, Sequence, or None, got {type(count)}") 1,-
176 _validate_split_field_length( 1bdefqnghijklmatsouprxz('vcw
177 options.coscheduled_sm_count, 1bdefqnghijklmatsouprxz('vcw
178 "coscheduled_sm_count",
179 n_groups,
180 count_is_scalar,
181 )
182 _validate_split_field_length( 1bdefqnghijklmatsouprxzvcw
183 options.preferred_coscheduled_sm_count, 1bdefqnghijklmatsouprxzvcw
184 "preferred_coscheduled_sm_count",
185 n_groups,
186 count_is_scalar,
187 )
188 _validate_split_field_length( 1bdefqnghijklmatsouprxzvcw
189 options.backfill, 1bdefqnghijklmatsouprxzvcw
190 "backfill",
191 n_groups,
192 count_is_scalar,
193 )
194 return n_groups 1bdefqnghijklmatsouprxzvcw
197cdef inline object _broadcast_field(object value, int n_groups):
198 if is_sequence(value): 1bdefqnghijklmatsouprxzvcw
199 return list(value) 1bqpr
200 return [value] * n_groups 1bdefqnghijklmatsouprxzvcw
203cdef inline unsigned int _to_sm_count(object value) except? 0:
204 """Convert a count value to unsigned int. None maps to 0 (discovery)."""
205 if value is None: 1bdefqnghijklmatsouprxzvcw
206 return 0 1defnghijklmatsoxvcw
207 if value < 0: 1bquprz
208 raise ValueError(f"count must be non-negative, got {value}") 1z
209 return <unsigned int>(value) 1bqupr
212cdef int _structured_split_checked = 0
214cdef inline bint _can_use_structured_sm_split():
215 """Whether the driver provides cuDevSmResourceSplit, a 13.1 driver API. Cached.
217 Every cuda-bindings at or above the 13.4 floor exports it. Only the driver can lack it."""
218 global _structured_split_checked
219 if _structured_split_checked != 0: 1bdefqnghijklmatsouprxzvcw
220 return _structured_split_checked == 1 1bdefqnghijklmatsouprxzvcw
221 IF CUDA_CORE_BUILD_MAJOR >= 13:
222 if cy_driver_version() >= (13, 1, 0): 1ac
223 _structured_split_checked = 1 1ac
224 return True 1ac
225 _structured_split_checked = -1
226 return False
229cdef object _resolve_split_by_count_request(SMResourceOptions options):
230 cdef int n_groups = _resolve_group_count(options)
231 cdef list counts = _broadcast_field(options.count, n_groups)
232 cdef object first = counts[0]
233 cdef object value
235 if options.coscheduled_sm_count is not None:
236 raise RuntimeError(
237 "SMResourceOptions.coscheduled_sm_count requires the CUDA 13.1 "
238 "structured SM split API"
239 )
240 if options.preferred_coscheduled_sm_count is not None:
241 raise RuntimeError(
242 "SMResourceOptions.preferred_coscheduled_sm_count requires the "
243 "CUDA 13.1 structured SM split API"
244 )
246 for value in counts[1:]:
247 if value != first:
248 raise RuntimeError(
249 "CUDA 12 SM splitting only supports homogeneous count values; "
250 "use CUDA 13.1 or newer for per-group counts"
251 )
253 cdef unsigned int min_count = _to_sm_count(first)
254 return n_groups, min_count
257IF CUDA_CORE_BUILD_MAJOR >= 13:
258 cdef inline int _fill_group_params(
259 cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params,
260 int n_groups,
261 SMResourceOptions options,
262 ) except?-1:
263 cdef list counts = _broadcast_field(options.count, n_groups) 1bdefqnghijklmatsouprxzvcw
264 cdef list coscheduled = _broadcast_field(options.coscheduled_sm_count, n_groups) 1bdefqnghijklmatsouprxzvcw
265 cdef list preferred = _broadcast_field(options.preferred_coscheduled_sm_count, n_groups) 1bdefqnghijklmatsouprxzvcw
266 cdef list backfills = _broadcast_field(options.backfill, n_groups) 1bdefqnghijklmatsouprxzvcw
267 cdef int i
269 for i in range(n_groups): 1bdefqnghijklmatsouprxzvcw
270 memset(¶ms[i], 0, sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)) 1bdefqnghijklmatsouprxzvcw
271 params[i].smCount = _to_sm_count(counts[i]) 1bdefqnghijklmatsouprxzvcw
272 if coscheduled[i] is not None: 1bdefqnghijklmatsouprxvcw
273 params[i].coscheduledSmCount = <unsigned int>(coscheduled[i]) 1s
274 if preferred[i] is not None: 1bdefqnghijklmatsouprxvcw
275 params[i].preferredCoscheduledSmCount = <unsigned int>(preferred[i])
276 params[i].flags = ( 1bdefqnghijklmatsouprxvcw
277 cydriver.CUdevSmResourceGroup_flags.CU_DEV_SM_RESOURCE_GROUP_BACKFILL
278 if backfills[i] else 0 1bdefqnghijklmatsouprxvcw
279 )
280 return 0 1bdefqnghijklmatsouprxvcw
283 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
284 cdef int n_groups = _resolve_group_count(options) 1bdefqnghijklmatsouprxzvcw
285 cdef cydriver.CUdevResource* result = NULL 1bdefqnghijklmatsouprxzvcw
286 cdef cydriver.CUdevResource remaining
287 cdef cydriver.CUdevResource synth
288 cdef cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params = NULL 1bdefqnghijklmatsouprxzvcw
289 cdef list groups = [] 1bdefqnghijklmatsouprxzvcw
290 cdef int i
292 params = <cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS*>malloc( 1bdefqnghijklmatsouprxzvcw
293 n_groups * sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)
294 )
295 if params == NULL: 1bdefqnghijklmatsouprxzvcw
296 raise MemoryError()
298 try: 1bdefqnghijklmatsouprxzvcw
299 _fill_group_params(params, n_groups, options) 1bdefqnghijklmatsouprxzvcw
301 if not dry_run: 1bdefqnghijklmatsouprxvcw
302 result = <cydriver.CUdevResource*>malloc( 1bdefqnghijklmatsouprvcw
303 n_groups * sizeof(cydriver.CUdevResource)
304 )
305 if result == NULL: 1bdefqnghijklmatsouprvcw
306 raise MemoryError()
308 memset(&remaining, 0, sizeof(cydriver.CUdevResource)) 1bdefqnghijklmatsouprxvcw
309 with nogil: 1bdefqnghijklmatsouprxvcw
310 HANDLE_RETURN(cydriver.cuDevSmResourceSplit( 1bdefqnghijklmatsouprxvcw
311 result,
312 <unsigned int>(n_groups),
313 &sm._resource,
314 &remaining,
315 0,
316 params,
317 ))
319 if result != NULL: 1bdefqnghijklmatsouprxvcw
320 for i in range(n_groups): 1bdefqnghijklmatsouprvcw
321 groups.append(SMResource._from_split_resource(result[i], sm, True)) 1bdefqnghijklmatsouprvcw
322 return groups, SMResource._from_split_resource(remaining, sm, True) 1bdefqnghijklmatsouprvcw
324 for i in range(n_groups): 1ox
325 memset(&synth, 0, sizeof(cydriver.CUdevResource)) 1ox
326 synth.type = cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM 1ox
327 synth.sm.smCount = params[i].smCount 1ox
328 groups.append(SMResource._from_split_resource(synth, sm, False)) 1ox
329 return groups, SMResource._from_split_resource(remaining, sm, False) 1ox
330 finally:
331 if params != NULL: 1bdefqnghijklmatsouprxvcw
332 free(params) 1bdefqnghijklmatsouprxzvcw
333 if result != NULL: 1bdefqnghijklmatsouprxzvcw
334 free(result) 1bdefqnghijklmatsouprvcw
335ELSE:
336 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
337 raise RuntimeError(
338 "SMResource.split() requires cuda.core to be built with CUDA 13.x bindings"
339 )
342cdef object _split_with_count_api(SMResource sm, SMResourceOptions options, bint dry_run):
343 cdef object request = _resolve_split_by_count_request(options)
344 cdef unsigned int nb_groups = <unsigned int>(request[0])
345 cdef unsigned int min_count = <unsigned int>(request[1])
346 cdef unsigned int actual_groups = nb_groups
347 cdef cydriver.CUdevResource* result = NULL
348 cdef cydriver.CUdevResource remaining
349 cdef list groups = []
350 cdef unsigned int i
352 result = <cydriver.CUdevResource*>malloc(nb_groups * sizeof(cydriver.CUdevResource))
353 if result == NULL:
354 raise MemoryError()
356 try:
357 memset(&remaining, 0, sizeof(cydriver.CUdevResource))
358 with nogil:
359 HANDLE_RETURN(cydriver.cuDevSmResourceSplitByCount(
360 result,
361 &actual_groups,
362 &sm._resource,
363 &remaining,
364 0,
365 min_count,
366 ))
368 for i in range(actual_groups):
369 if dry_run:
370 groups.append(SMResource._from_split_resource(result[i], sm, False))
371 else:
372 groups.append(SMResource._from_split_resource(result[i], sm, True))
373 if dry_run:
374 return groups, SMResource._from_split_resource(remaining, sm, False)
375 return groups, SMResource._from_split_resource(remaining, sm, True)
376 finally:
377 free(result)
380cdef inline unsigned int _sm_resource_granularity(int device_id) except? 0:
381 cdef int major
383 with nogil:
384 HANDLE_RETURN(cydriver.cuDeviceGetAttribute(
385 &major,
386 cydriver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
387 <cydriver.CUdevice>(device_id),
388 ))
389 if major >= 9:
390 return 8
391 return 2
394cdef inline unsigned int _fallback_if_zero(unsigned int value, unsigned int fallback) noexcept:
395 if value != 0: 1bdefqnghijklmatsouprxvcw
396 return value 1bdefqnghijklmatsouprvcw
397 return fallback 1defnghijklmatsopxvcw
400cdef class SMResource:
401 """Represent an SM (streaming multiprocessor) resource partition.
403 Instances are returned by :obj:`DeviceResources.sm` or
404 :meth:`SMResource.split` and cannot be instantiated directly.
405 """
407 def __init__(self, *args, **kwargs):
408 raise RuntimeError( 1;
409 "SMResource cannot be instantiated directly. "
410 "Use dev.resources.sm or SMResource.split()."
411 )
413 @staticmethod
414 cdef SMResource _from_dev_resource(cydriver.CUdevResource res, int device_id):
415 cdef SMResource self = SMResource.__new__(SMResource) 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
416 self._resource = res 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
417 self._sm_count = res.sm.smCount 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
418 IF CUDA_CORE_BUILD_MAJOR >= 13:
419 self._min_partition_size = res.sm.minSmPartitionSize 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
420 self._coscheduled_alignment = res.sm.smCoscheduledAlignment 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
421 self._flags = res.sm.flags 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
422 ELSE:
423 self._min_partition_size = _sm_resource_granularity(device_id)
424 self._coscheduled_alignment = self._min_partition_size
425 self._flags = 0
426 self._is_usable = True 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
427 return self 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
429 @staticmethod
430 cdef SMResource _from_split_resource(cydriver.CUdevResource res, SMResource parent, bint is_usable):
431 cdef SMResource self = SMResource.__new__(SMResource) 1bdefqnghijklmatsouprxvcw
432 self._resource = res 1bdefqnghijklmatsouprxvcw
433 self._sm_count = res.sm.smCount 1bdefqnghijklmatsouprxvcw
434 IF CUDA_CORE_BUILD_MAJOR >= 13:
435 self._min_partition_size = _fallback_if_zero( 1bdefqnghijklmatsouprxvcw
436 res.sm.minSmPartitionSize,
437 parent._min_partition_size,
438 )
439 self._coscheduled_alignment = _fallback_if_zero( 1bdefqnghijklmatsouprxvcw
440 res.sm.smCoscheduledAlignment,
441 parent._coscheduled_alignment,
442 )
443 self._flags = res.sm.flags 1bdefqnghijklmatsouprxvcw
444 ELSE:
445 self._min_partition_size = parent._min_partition_size
446 self._coscheduled_alignment = parent._coscheduled_alignment
447 self._flags = parent._flags
448 self._is_usable = is_usable 1bdefqnghijklmatsouprxvcw
449 return self 1bdefqnghijklmatsouprxvcw
451 @property
452 def handle(self) -> int:
453 """Return the address of the underlying ``CUdevResource`` struct."""
454 return <intptr_t>(&self._resource) 1:
456 @property
457 def sm_count(self) -> int:
458 """Total SMs available in this resource."""
459 return self._sm_count 1bEDyq:tsoupr
461 @property
462 def min_partition_size(self) -> int:
463 """Minimum SM count required to create a partition."""
464 return self._min_partition_size 1bq?:tupr('
466 @property
467 def coscheduled_alignment(self) -> int:
468 """Number of SMs guaranteed to be co-scheduled."""
469 return self._coscheduled_alignment 1?:s
471 @property
472 def flags(self) -> int:
473 """Raw flags from the underlying SM resource."""
474 return self._flags 1:
476 def split(
477 self,
478 SMResourceOptions options not None: SMResourceOptions,
479 *,
480 bint dry_run=False
481 ) -> tuple[list[SMResource], SMResource]:
482 """Split this SM resource into groups and a remainder.
484 Parameters
485 ----------
486 options : :obj:`SMResourceOptions`
487 Split configuration (count, co-scheduling constraints).
488 dry_run : bool, optional
489 If ``True``, return filled-in metadata without creating
490 usable resource objects. (Default to ``False``)
492 Returns
493 -------
494 tuple[list[:obj:`SMResource`], :obj:`SMResource`]
495 ``(groups, remainder)`` where each group holds a disjoint
496 SM partition and *remainder* holds any unassigned SMs.
497 """
498 cdef SMResourceOptions opts = check_or_create_options( 1bdefqnghijklmatsoupr,-x*z('vcw
499 SMResourceOptions, options, "SM resource options"
500 )
501 _resolve_group_count(opts) 1bdefqnghijklmatsoupr,-x*z('vcw
502 _check_green_ctx_support() 1bdefqnghijklmatsouprxzvcw
503 if _can_use_structured_sm_split(): 1bdefqnghijklmatsouprxzvcw
504 return _split_with_general_api(self, opts, dry_run) 1bdefqnghijklmatsouprxzvcw
505 # SplitByCount requires the same 12.4+ as green ctx support (already checked above)
506 return _split_with_count_api(self, opts, dry_run)
509cdef class WorkqueueResource:
510 """Represent a workqueue resource for a device or green context.
512 Merges ``CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG`` and
513 ``CU_DEV_RESOURCE_TYPE_WORKQUEUE`` under one user-facing type.
514 Instances are returned by :obj:`DeviceResources.workqueue` and
515 cannot be instantiated directly.
516 """
518 def __init__(self, *args, **kwargs) -> None:
519 raise RuntimeError( 1;
520 "WorkqueueResource cannot be instantiated directly. "
521 "Use dev.resources.workqueue."
522 )
524 @staticmethod
525 cdef WorkqueueResource _from_dev_resources(
526 cydriver.CUdevResource wq_config,
527 cydriver.CUdevResource wq,
528 ):
529 cdef WorkqueueResource self = WorkqueueResource.__new__(WorkqueueResource) 1B2yA345789!
530 self._wq_config_resource = wq_config 1B2yA345789!
531 self._wq_resource = wq 1B2yA345789!
532 return self 1B2yA345789!
534 @property
535 def handle(self) -> int:
536 """Return the address of the underlying config ``CUdevResource`` struct."""
537 return <intptr_t>(&self._wq_config_resource) 1By+
539 @property
540 def sharing_scope(self) -> WorkqueueSharingScopeType:
541 """Current sharing scope of this workqueue resource.
543 Returns the :class:`~cuda.core.typing.WorkqueueSharingScopeType`
544 member corresponding to the driver-populated
545 ``wqConfig.sharingScope`` field. It can be updated via
546 :meth:`configure` with
547 :attr:`WorkqueueResourceOptions.sharing_scope`.
548 """
549 IF CUDA_CORE_BUILD_MAJOR >= 13:
550 from cuda.core.typing import WorkqueueSharingScopeType 1B61+
551 cdef object scope = self._wq_config_resource.wqConfig.sharingScope 1B61+
552 if scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX: 1B61+
553 return WorkqueueSharingScopeType.DEVICE_CTX 1B6+
554 elif scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED: 11
555 return WorkqueueSharingScopeType.GREEN_CTX_BALANCED 11
556 raise RuntimeError(f"Unknown sharing scope enum value: {scope}")
557 ELSE:
558 raise RuntimeError(
559 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
560 )
562 @property
563 def concurrency_limit(self) -> int:
564 """Current expected maximum concurrent stream-ordered workloads.
566 Reflects the driver-populated ``wqConfig.wqConcurrencyLimit`` field.
567 When first queried from a device, this matches the driver-reported
568 cap (typically ``CUDA_DEVICE_MAX_CONNECTIONS``). It can be updated
569 via :meth:`configure` with
570 :attr:`WorkqueueResourceOptions.concurrency_limit`.
571 """
572 IF CUDA_CORE_BUILD_MAJOR >= 13:
573 return self._wq_config_resource.wqConfig.wqConcurrencyLimit 1B$+
574 ELSE:
575 raise RuntimeError(
576 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
577 )
579 @property
580 def device(self) -> Device:
581 """The :class:`~cuda.core.Device` this workqueue resource is available on."""
582 IF CUDA_CORE_BUILD_MAJOR >= 13:
583 from cuda.core._device import Device # avoid circular import 1B+
584 return Device(int(self._wq_config_resource.wqConfig.device)) 1B+
585 ELSE:
586 raise RuntimeError(
587 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
588 )
590 def configure(self, WorkqueueResourceOptions options not None: WorkqueueResourceOptions) -> None:
591 """Configure the workqueue resource in place.
593 Parameters
594 ----------
595 options : :obj:`WorkqueueResourceOptions`
596 Configuration options (sharing scope, concurrency limit).
597 """
598 cdef WorkqueueResourceOptions opts = check_or_create_options( 1n#$)61%
599 WorkqueueResourceOptions, options, "Workqueue resource options"
600 )
601 _check_green_ctx_support() 1n#$)61%
602 _check_workqueue_support() 1n#$)61%
603 if opts.sharing_scope is None and opts.concurrency_limit is None: 1n#$)61%
604 return None 1)
606 IF CUDA_CORE_BUILD_MAJOR >= 13:
607 if opts.concurrency_limit is not None: 1n#$61%
608 self._wq_config_resource.wqConfig.wqConcurrencyLimit = ( 1n#$
609 <unsigned int>opts.concurrency_limit 1n#$
610 )
611 if opts.sharing_scope == "device_ctx": 1n#$61%
612 self._wq_config_resource.wqConfig.sharingScope = ( 16
613 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX
614 )
615 elif opts.sharing_scope == "green_ctx_balanced": 1n#$1%
616 self._wq_config_resource.wqConfig.sharingScope = ( 1n#1%
617 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED
618 )
619 ELSE:
620 raise RuntimeError(
621 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
622 )
625cdef class DeviceResources:
626 """Namespace for hardware resource queries.
628 When obtained via :obj:`Device.resources`, queries return full device
629 resources. When obtained via :obj:`Context.resources` or
630 :obj:`Stream.resources`, queries return the resources provisioned for
631 that context.
633 This class cannot be instantiated directly.
634 """
636 def __init__(self, *args, **kwargs) -> None:
637 raise RuntimeError( 1;
638 "DeviceResources cannot be instantiated directly. "
639 "Use dev.resources or ctx.resources."
640 )
642 @staticmethod
643 cdef DeviceResources _init(int device_id):
644 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1FdG2efHAghijklmaICJKLMNOPQRSTUVWXY345789!Zc0
645 self._device_id = device_id 1FdG2efHAghijklmaICJKLMNOPQRSTUVWXY345789!Zc0
646 # _h_context is default empty — queries use cuDeviceGetDevResource
647 return self 1FdG2efHAghijklmaICJKLMNOPQRSTUVWXY345789!Zc0
649 @staticmethod
650 cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id):
651 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1bEDBy
652 self._device_id = device_id 1bEDBy
653 self._h_context = h_context 1bEDBy
654 return self 1bEDBy
656 cdef inline int _query_sm(self, cydriver.CUdevResource* res) except?-1 nogil:
657 """Query SM resource from either device or context."""
658 cdef GreenCtxHandle h_green
659 if self._h_context: 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
660 h_green = get_context_green_ctx(self._h_context) 1bEDy
661 if h_green: 1bEDy
662 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1bEy
663 as_cu(h_green), res,
664 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
665 ))
666 else:
667 HANDLE_RETURN(cydriver.cuCtxGetDevResource( 1D
668 as_cu(self._h_context), res,
669 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1D
670 ))
671 else:
672 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1FdGefHAghijklmaICJKLMNOPQRSTUVWXYZc0
673 <cydriver.CUdevice>(self._device_id), res,
674 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1FdGefHAghijklmaICJKLMNOPQRSTUVWXYZc0
675 ))
676 return 0 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
678 @property
679 def sm(self) -> SMResource:
680 """Return the :obj:`SMResource` for this device or context."""
681 _check_green_ctx_support() 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
682 cdef cydriver.CUdevResource res
683 with nogil: 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
684 self._query_sm(&res) 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
685 return SMResource._from_dev_resource(res, self._device_id) 1bFEdDGyefHAghijklmaICJKLMNOPQRSTUVWXYZc0
687 @property
688 def workqueue(self) -> WorkqueueResource:
689 """Return the :obj:`WorkqueueResource` for this device or context."""
690 _check_green_ctx_support() 1B2yA345789!
691 _check_workqueue_support() 1B2yA345789!
692 cdef cydriver.CUdevResource _wq_config
693 cdef cydriver.CUdevResource _wq
695 IF CUDA_CORE_BUILD_MAJOR >= 13:
696 cdef GreenCtxHandle h_green
697 if self._h_context: 1B2yA345789!
698 h_green = get_context_green_ctx(self._h_context) 1By
699 if h_green: 1By
700 # Green context query
701 with nogil: 1y
702 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1y
703 as_cu(h_green),
704 &_wq_config,
705 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
706 ))
707 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1y
708 as_cu(h_green),
709 &_wq,
710 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
711 ))
712 else:
713 # Primary context query
714 with nogil: 1B
715 HANDLE_RETURN(cydriver.cuCtxGetDevResource( 1B
716 as_cu(self._h_context),
717 &_wq_config,
718 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
719 ))
720 HANDLE_RETURN(cydriver.cuCtxGetDevResource( 1B
721 as_cu(self._h_context),
722 &_wq,
723 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
724 ))
725 else:
726 # Device-level query
727 with nogil: 12A345789!
728 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 12A345789!
729 <cydriver.CUdevice>(self._device_id),
730 &_wq_config,
731 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
732 ))
733 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 12A345789!
734 <cydriver.CUdevice>(self._device_id),
735 &_wq,
736 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
737 ))
738 return WorkqueueResource._from_dev_resources(_wq_config, _wq) 1B2yA345789!
739 ELSE:
740 raise RuntimeError(
741 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
742 )