Coverage for cuda/core/_device_resources.pyx: 76.22%
307 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +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._resource_handles 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_binding_version, 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: 1byxcvdenzkwfghiajABCDpEqFlGrHmIoJsKtLMNXPYQ2RSTOUZVW
46 return 0 1byxcvdenzkwfghiajABCDpEqFlGrHmIoJsKtLMNXPYQ2RSTOUZVW
47 if _green_ctx_checked == -1: 1a
48 raise RuntimeError(_green_ctx_err_msg)
49 cdef tuple drv = cy_driver_version() 1a
50 cdef tuple bind = cy_binding_version() 1a
51 if drv < (12, 4, 0): 1a
52 _green_ctx_err_msg = (
53 "Green context support requires CUDA driver 12.4 or newer "
54 f"(current driver: {'.'.join(map(str, drv))})"
55 )
56 _green_ctx_checked = -1
57 raise RuntimeError(_green_ctx_err_msg)
58 if bind < (12, 4, 0): 1a
59 _green_ctx_err_msg = (
60 "Green context support requires cuda.bindings 12.4 or newer "
61 f"(current bindings: {'.'.join(map(str, bind))})"
62 )
63 _green_ctx_checked = -1
64 raise RuntimeError(_green_ctx_err_msg)
65 _green_ctx_checked = 1 1a
66 return 0 1a
69cdef inline int _check_workqueue_support() except?-1:
70 global _workqueue_checked, _workqueue_err_msg
71 if _workqueue_checked == 1: 1vkwXPYQ2RSTOUZVW
72 return 0 1vkXPYQ2RSTOUZVW
73 if _workqueue_checked == -1: 1w
74 raise RuntimeError(_workqueue_err_msg)
75 cdef tuple drv = cy_driver_version() 1w
76 cdef tuple bind = cy_binding_version() 1w
77 if drv < (13, 1, 0): 1w
78 _workqueue_err_msg = (
79 "WorkqueueResource requires CUDA driver 13.1 or newer "
80 f"(current driver: {'.'.join(map(str, drv))})"
81 )
82 _workqueue_checked = -1
83 raise RuntimeError(_workqueue_err_msg)
84 if bind < (13, 1, 0): 1w
85 _workqueue_err_msg = (
86 "WorkqueueResource requires cuda.bindings 13.1 or newer "
87 f"(current bindings: {'.'.join(map(str, bind))})"
88 )
89 _workqueue_checked = -1
90 raise RuntimeError(_workqueue_err_msg)
91 _workqueue_checked = 1 1w
92 return 0 1w
95@dataclass
96cdef class SMResourceOptions:
97 """Customizable :obj:`SMResource.split` options.
99 Each field accepts a scalar (for a single group) or a ``Sequence``
100 (for multiple groups). ``count`` drives the number of groups; other
101 ``Sequence`` fields must match its length.
103 Attributes
104 ----------
105 count : int or Sequence[int], optional
106 Requested SM count per group. ``None`` means discovery mode
107 (auto-detect). (Default to ``None``)
108 coscheduled_sm_count : int or Sequence[int], optional
109 Minimum number of SMs guaranteed to be co-scheduled in each
110 group. (Default to ``None``)
111 preferred_coscheduled_sm_count : int or Sequence[int], optional
112 Preferred co-scheduled SM count; the driver tries to satisfy
113 this but may fall back to ``coscheduled_sm_count``.
114 (Default to ``None``)
115 backfill : bool or Sequence[bool], optional
116 If ``True``, allow the driver to relax the co-scheduling
117 constraint when assigning SMs. This enables requesting
118 arbitrary aligned SM counts that the driver would otherwise
119 reject due to hardware topology constraints.
120 (Default to ``False``)
121 """
123 count: int | SequenceABC[int] | None = None
124 coscheduled_sm_count: int | SequenceABC[int] | None = None
125 preferred_coscheduled_sm_count: int | SequenceABC[int] | None = None
126 backfill: bool | SequenceABC[bool] = False
129@dataclass
130cdef class WorkqueueResourceOptions:
131 """Customizable :obj:`WorkqueueResource.configure` options.
133 Attributes
134 ----------
135 sharing_scope : :class:`~cuda.core.typing.WorkqueueSharingScopeType` | str, optional
136 Workqueue sharing scope. Accepted values: ``"device_ctx"`` or
137 ``"green_ctx_balanced"``.
138 concurrency_limit : int, optional
139 Expected maximum number of concurrent stream-ordered
140 workloads. Must be ``>= 1`` when set. The effective
141 driver-side cap is ``CUDA_DEVICE_MAX_CONNECTIONS``
142 (typically ``[1, 32]``); configurations may exceed
143 this cap, but the driver will not guarantee that work
144 submission remains non-overlapping. (Default to ``None``)
145 """
147 sharing_scope: WorkqueueSharingScopeType | str | None = None
148 concurrency_limit: int | None = None
150 def __post_init__(self):
151 from cuda.core.typing import WorkqueueSharingScopeType 1k45XY2SOZ8
152 check_str_enum(self.sharing_scope, WorkqueueSharingScopeType, allow_none=True) 1k45XY2SOZ8
153 if self.concurrency_limit is not None and self.concurrency_limit < 1: 1k45XY2SOZ
154 raise ValueError( 145
155 f"concurrency_limit must be >= 1, got {self.concurrency_limit}" 145
156 )
159cdef inline int _validate_split_field_length(
160 object value, str field_name, int n_groups, bint count_is_scalar
161) except?-1:
162 if count_is_scalar: 1bcdenkfghiajpqlrmost10
163 if is_sequence(value): 1cdekfghiajpqlrst1
164 raise ValueError( 11
165 f"{field_name} is a Sequence but count is scalar; " 11
166 "count must be a Sequence to specify multiple groups"
167 )
168 elif is_sequence(value) and len(value) != n_groups: 1bnmo0
169 raise ValueError( 10
170 f"{field_name} has length {len(value)}, expected {n_groups} " 10
171 "(must match count)"
172 )
173 return 0 1bcdenkfghiajpqlrmost
176cdef inline int _resolve_group_count(SMResourceOptions options) except?-1:
177 cdef object count = options.count 1bcdenkfghiajpqlrmost10
178 cdef int n_groups
179 cdef bint count_is_scalar
181 if count is None or isinstance(count, int): 1bcdenkfghiajpqlrmost10
182 n_groups = 1 1cdekfghiajpqlrst1
183 count_is_scalar = True 1cdekfghiajpqlrst1
184 elif is_sequence(count): 1bnmo0
185 n_groups = len(count) 1bnmo0
186 if n_groups == 0: 1bnmo0
187 raise ValueError("count sequence must not be empty")
188 count_is_scalar = False 1bnmo0
189 else:
190 raise TypeError(f"count must be int, Sequence, or None, got {type(count)}")
192 _validate_split_field_length( 1bcdenkfghiajpqlrmost10
193 options.coscheduled_sm_count, 1bcdenkfghiajpqlrmost10
194 "coscheduled_sm_count",
195 n_groups,
196 count_is_scalar,
197 )
198 _validate_split_field_length( 1bcdenkfghiajpqlrmost
199 options.preferred_coscheduled_sm_count, 1bcdenkfghiajpqlrmost
200 "preferred_coscheduled_sm_count",
201 n_groups,
202 count_is_scalar,
203 )
204 _validate_split_field_length( 1bcdenkfghiajpqlrmost
205 options.backfill, 1bcdenkfghiajpqlrmost
206 "backfill",
207 n_groups,
208 count_is_scalar,
209 )
210 return n_groups 1bcdenkfghiajpqlrmost
213cdef inline object _broadcast_field(object value, int n_groups):
214 if is_sequence(value): 1bcdenkfghiajpqlrmost
215 return list(value) 1bnmo
216 return [value] * n_groups 1bcdenkfghiajpqlrmost
219cdef inline unsigned int _to_sm_count(object value) except? 0:
220 """Convert a count value to unsigned int. None maps to 0 (discovery)."""
221 if value is None: 1bcdenkfghiajpqlrmost
222 return 0 1cdekfghiajpqls
223 if value < 0: 1bnrmot
224 raise ValueError(f"count must be non-negative, got {value}") 1t
225 return <unsigned int>(value) 1bnrmo
228IF CUDA_CORE_BUILD_MAJOR >= 13:
229 from cuda.core._resource_handles cimport sm_resource_split, has_sm_resource_split
231cdef int _structured_split_checked = 0
233cdef inline bint _can_use_structured_sm_split():
234 """Check if cuDevSmResourceSplit (13.1+) is available. Cached."""
235 global _structured_split_checked
236 if _structured_split_checked != 0: 1bcdenkfghiajpqlrmost
237 return _structured_split_checked == 1 1bcdenkfghijpqlrmost
238 IF CUDA_CORE_BUILD_MAJOR >= 13:
239 if (has_sm_resource_split() 1a
240 and cy_driver_version() >= (13, 1, 0) 1a
241 and cy_binding_version() >= (13, 1, 0)): 1a
242 _structured_split_checked = 1 1a
243 return True 1a
244 _structured_split_checked = -1
245 return False
248cdef object _resolve_split_by_count_request(SMResourceOptions options):
249 cdef int n_groups = _resolve_group_count(options)
250 cdef list counts = _broadcast_field(options.count, n_groups)
251 cdef object first = counts[0]
252 cdef object value
253 cdef unsigned int min_count
255 if options.coscheduled_sm_count is not None:
256 raise RuntimeError(
257 "SMResourceOptions.coscheduled_sm_count requires the CUDA 13.1 "
258 "structured SM split API"
259 )
260 if options.preferred_coscheduled_sm_count is not None:
261 raise RuntimeError(
262 "SMResourceOptions.preferred_coscheduled_sm_count requires the "
263 "CUDA 13.1 structured SM split API"
264 )
266 for value in counts[1:]:
267 if value != first:
268 raise RuntimeError(
269 "CUDA 12 SM splitting only supports homogeneous count values; "
270 "use CUDA 13.1 or newer for per-group counts"
271 )
273 min_count = _to_sm_count(first)
274 return n_groups, min_count
277IF CUDA_CORE_BUILD_MAJOR >= 13:
278 cdef inline int _fill_group_params(
279 cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params,
280 int n_groups,
281 SMResourceOptions options,
282 ) except?-1:
283 cdef list counts = _broadcast_field(options.count, n_groups) 1bcdenkfghiajpqlrmost
284 cdef list coscheduled = _broadcast_field(options.coscheduled_sm_count, n_groups) 1bcdenkfghiajpqlrmost
285 cdef list preferred = _broadcast_field(options.preferred_coscheduled_sm_count, n_groups) 1bcdenkfghiajpqlrmost
286 cdef list backfills = _broadcast_field(options.backfill, n_groups) 1bcdenkfghiajpqlrmost
287 cdef int i
289 for i in range(n_groups): 1bcdenkfghiajpqlrmost
290 memset(¶ms[i], 0, sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)) 1bcdenkfghiajpqlrmost
291 params[i].smCount = _to_sm_count(counts[i]) 1bcdenkfghiajpqlrmost
292 if coscheduled[i] is not None: 1bcdenkfghiajpqlrmos
293 params[i].coscheduledSmCount = <unsigned int>(coscheduled[i])
294 if preferred[i] is not None: 1bcdenkfghiajpqlrmos
295 params[i].preferredCoscheduledSmCount = <unsigned int>(preferred[i])
296 params[i].flags = ( 1bcdenkfghiajpqlrmos
297 cydriver.CUdevSmResourceGroup_flags.CU_DEV_SM_RESOURCE_GROUP_BACKFILL
298 if backfills[i] else 0 1bcdenkfghiajpqlrmos
299 )
300 return 0 1bcdenkfghiajpqlrmos
303 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
304 cdef int n_groups = _resolve_group_count(options) 1bcdenkfghiajpqlrmost
305 cdef cydriver.CUdevResource* result = NULL 1bcdenkfghiajpqlrmost
306 cdef cydriver.CUdevResource remaining
307 cdef cydriver.CUdevResource synth
308 cdef cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params = NULL 1bcdenkfghiajpqlrmost
309 cdef list groups = [] 1bcdenkfghiajpqlrmost
310 cdef int i
312 params = <cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS*>malloc( 1bcdenkfghiajpqlrmost
313 n_groups * sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)
314 )
315 if params == NULL: 1bcdenkfghiajpqlrmost
316 raise MemoryError()
318 try: 1bcdenkfghiajpqlrmost
319 _fill_group_params(params, n_groups, options) 1bcdenkfghiajpqlrmost
321 if not dry_run: 1bcdenkfghiajpqlrmos
322 result = <cydriver.CUdevResource*>malloc( 1bcdenkfghiajpqlrmo
323 n_groups * sizeof(cydriver.CUdevResource)
324 )
325 if result == NULL: 1bcdenkfghiajpqlrmo
326 raise MemoryError()
328 memset(&remaining, 0, sizeof(cydriver.CUdevResource)) 1bcdenkfghiajpqlrmos
329 with nogil: 1bcdenkfghiajpqlrmos
330 HANDLE_RETURN(sm_resource_split( 1bcdenkfghiajpqlrmos
331 result,
332 <unsigned int>(n_groups),
333 &sm._resource,
334 &remaining,
335 0,
336 <void*>params,
337 ))
339 if result != NULL: 1bcdenkfghiajpqlrmos
340 for i in range(n_groups): 1bcdenkfghiajpqlrmo
341 groups.append(SMResource._from_split_resource(result[i], sm, True)) 1bcdenkfghiajpqlrmo
342 return groups, SMResource._from_split_resource(remaining, sm, True) 1bcdenkfghiajpqlrmo
344 for i in range(n_groups): 1ls
345 memset(&synth, 0, sizeof(cydriver.CUdevResource)) 1ls
346 synth.type = cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM 1ls
347 synth.sm.smCount = params[i].smCount 1ls
348 groups.append(SMResource._from_split_resource(synth, sm, False)) 1ls
349 return groups, SMResource._from_split_resource(remaining, sm, False) 1ls
350 finally:
351 if params != NULL: 1bcdenkfghiajpqlrmos
352 free(params) 1bcdenkfghiajpqlrmost
353 if result != NULL: 1bcdenkfghiajpqlrmost
354 free(result) 1bcdenkfghiajpqlrmo
355ELSE:
356 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
357 raise RuntimeError(
358 "SMResource.split() requires cuda.core to be built with CUDA 13.x bindings"
359 )
362cdef object _split_with_count_api(SMResource sm, SMResourceOptions options, bint dry_run):
363 cdef object request = _resolve_split_by_count_request(options)
364 cdef unsigned int nb_groups = <unsigned int>(request[0])
365 cdef unsigned int min_count = <unsigned int>(request[1])
366 cdef unsigned int actual_groups = nb_groups
367 cdef cydriver.CUdevResource* result = NULL
368 cdef cydriver.CUdevResource remaining
369 cdef list groups = []
370 cdef int i
372 result = <cydriver.CUdevResource*>malloc(nb_groups * sizeof(cydriver.CUdevResource))
373 if result == NULL:
374 raise MemoryError()
376 try:
377 memset(&remaining, 0, sizeof(cydriver.CUdevResource))
378 with nogil:
379 HANDLE_RETURN(cydriver.cuDevSmResourceSplitByCount(
380 result,
381 &actual_groups,
382 &sm._resource,
383 &remaining,
384 0,
385 min_count,
386 ))
388 for i in range(actual_groups):
389 if dry_run:
390 groups.append(SMResource._from_split_resource(result[i], sm, False))
391 else:
392 groups.append(SMResource._from_split_resource(result[i], sm, True))
393 if dry_run:
394 return groups, SMResource._from_split_resource(remaining, sm, False)
395 return groups, SMResource._from_split_resource(remaining, sm, True)
396 finally:
397 free(result)
400cdef inline unsigned int _sm_resource_granularity(int device_id) except? 0:
401 cdef int major
403 with nogil:
404 HANDLE_RETURN(cydriver.cuDeviceGetAttribute(
405 &major,
406 cydriver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
407 <cydriver.CUdevice>(device_id),
408 ))
409 if major >= 9:
410 return 8
411 return 2
414cdef inline unsigned int _fallback_if_zero(unsigned int value, unsigned int fallback) noexcept:
415 if value != 0: 1bcdenkfghiajpqlrmos
416 return value 1bcdenkfghiajpqlrmo
417 return fallback 1cdekfghiajpqlms
420cdef class SMResource:
421 """Represent an SM (streaming multiprocessor) resource partition.
423 Instances are returned by :obj:`DeviceResources.sm` or
424 :meth:`SMResource.split` and cannot be instantiated directly.
425 """
427 def __init__(self, *args, **kwargs):
428 raise RuntimeError( 17
429 "SMResource cannot be instantiated directly. "
430 "Use dev.resources.sm or SMResource.split()."
431 )
433 @staticmethod
434 cdef SMResource _from_dev_resource(cydriver.CUdevResource res, int device_id):
435 cdef SMResource self = SMResource.__new__(SMResource) 1byxcvdezwfghiajABCDEFGHIJKLMN
436 self._resource = res 1byxcvdezwfghiajABCDEFGHIJKLMN
437 self._sm_count = res.sm.smCount 1byxcvdezwfghiajABCDEFGHIJKLMN
438 IF CUDA_CORE_BUILD_MAJOR >= 13:
439 self._min_partition_size = res.sm.minSmPartitionSize 1byxcvdezwfghiajABCDEFGHIJKLMN
440 self._coscheduled_alignment = res.sm.smCoscheduledAlignment 1byxcvdezwfghiajABCDEFGHIJKLMN
441 self._flags = res.sm.flags 1byxcvdezwfghiajABCDEFGHIJKLMN
442 ELSE:
443 self._min_partition_size = _sm_resource_granularity(device_id)
444 self._coscheduled_alignment = self._min_partition_size
445 self._flags = 0
446 self._is_usable = True 1byxcvdezwfghiajABCDEFGHIJKLMN
447 return self 1byxcvdezwfghiajABCDEFGHIJKLMN
449 @staticmethod
450 cdef SMResource _from_split_resource(cydriver.CUdevResource res, SMResource parent, bint is_usable):
451 cdef SMResource self = SMResource.__new__(SMResource) 1bcdenkfghiajpqlrmos
452 self._resource = res 1bcdenkfghiajpqlrmos
453 self._sm_count = res.sm.smCount 1bcdenkfghiajpqlrmos
454 IF CUDA_CORE_BUILD_MAJOR >= 13:
455 self._min_partition_size = _fallback_if_zero( 1bcdenkfghiajpqlrmos
456 res.sm.minSmPartitionSize,
457 parent._min_partition_size,
458 )
459 self._coscheduled_alignment = _fallback_if_zero( 1bcdenkfghiajpqlrmos
460 res.sm.smCoscheduledAlignment,
461 parent._coscheduled_alignment,
462 )
463 self._flags = res.sm.flags 1bcdenkfghiajpqlrmos
464 ELSE:
465 self._min_partition_size = parent._min_partition_size
466 self._coscheduled_alignment = parent._coscheduled_alignment
467 self._flags = parent._flags
468 self._is_usable = is_usable 1bcdenkfghiajpqlrmos
469 return self 1bcdenkfghiajpqlrmos
471 @property
472 def handle(self) -> int:
473 """Return the address of the underlying ``CUdevResource`` struct."""
474 return <intptr_t>(&self._resource) 16
476 @property
477 def sm_count(self) -> int:
478 """Total SMs available in this resource."""
479 return self._sm_count 1bxvn6pqlrmo
481 @property
482 def min_partition_size(self) -> int:
483 """Minimum SM count required to create a partition."""
484 return self._min_partition_size 1bn96prmo10
486 @property
487 def coscheduled_alignment(self) -> int:
488 """Number of SMs guaranteed to be co-scheduled."""
489 return self._coscheduled_alignment 196q
491 @property
492 def flags(self) -> int:
493 """Raw flags from the underlying SM resource."""
494 return self._flags 16
496 def split(
497 self,
498 options: SMResourceOptions,
499 *,
500 bint dry_run=False
501 ) -> tuple[list[SMResource], SMResource]:
502 """Split this SM resource into groups and a remainder.
504 Parameters
505 ----------
506 options : :obj:`SMResourceOptions`
507 Split configuration (count, co-scheduling constraints).
508 dry_run : bool, optional
509 If ``True``, return filled-in metadata without creating
510 usable resource objects. (Default to ``False``)
512 Returns
513 -------
514 tuple[list[:obj:`SMResource`], :obj:`SMResource`]
515 ``(groups, remainder)`` where each group holds a disjoint
516 SM partition and *remainder* holds any unassigned SMs.
517 """
518 cdef SMResourceOptions opts = check_or_create_options( 1bcdenkfghiajpqlrmost10
519 SMResourceOptions, options, "SM resource options"
520 )
521 _resolve_group_count(opts) 1bcdenkfghiajpqlrmost10
522 _check_green_ctx_support() 1bcdenkfghiajpqlrmost
523 with self._split_mutex: 1bcdenkfghiajpqlrmost
524 if _can_use_structured_sm_split(): 1bcdenkfghiajpqlrmost
525 return _split_with_general_api(self, opts, dry_run) 1bcdenkfghiajpqlrmost
526 # SplitByCount requires the same 12.4+ as green ctx support (already checked above)
527 return _split_with_count_api(self, opts, dry_run)
530cdef class WorkqueueResource:
531 """Represent a workqueue resource for a device or green context.
533 Merges ``CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG`` and
534 ``CU_DEV_RESOURCE_TYPE_WORKQUEUE`` under one user-facing type.
535 Instances are returned by :obj:`DeviceResources.workqueue` and
536 cannot be instantiated directly.
537 """
539 def __init__(self, *args, **kwargs) -> None:
540 raise RuntimeError( 17
541 "WorkqueueResource cannot be instantiated directly. "
542 "Use dev.resources.workqueue."
543 )
545 @staticmethod
546 cdef WorkqueueResource _from_dev_resources(
547 cydriver.CUdevResource wq_config,
548 cydriver.CUdevResource wq,
549 ):
550 cdef WorkqueueResource self = WorkqueueResource.__new__(WorkqueueResource) 1vwPQRTUVW
551 self._wq_config_resource = wq_config 1vwPQRTUVW
552 self._wq_resource = wq 1vwPQRTUVW
553 return self 1vwPQRTUVW
555 @property
556 def handle(self) -> int:
557 """Return the address of the underlying config ``CUdevResource`` struct."""
558 return <intptr_t>(&self._wq_config_resource) 1v3
560 @property
561 def sharing_scope(self) -> WorkqueueSharingScopeType:
562 """Current sharing scope of this workqueue resource.
564 Returns the :class:`~cuda.core.typing.WorkqueueSharingScopeType`
565 member corresponding to the driver-populated
566 ``wqConfig.sharingScope`` field. It can be updated via
567 :meth:`configure` with
568 :attr:`WorkqueueResourceOptions.sharing_scope`.
569 """
570 IF CUDA_CORE_BUILD_MAJOR >= 13:
571 from cuda.core.typing import WorkqueueSharingScopeType 1SO3
572 cdef object scope = self._wq_config_resource.wqConfig.sharingScope 1SO3
573 if scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX: 1SO3
574 return WorkqueueSharingScopeType.DEVICE_CTX 1S3
575 elif scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED: 1O
576 return WorkqueueSharingScopeType.GREEN_CTX_BALANCED 1O
577 raise RuntimeError(f"Unknown sharing scope enum value: {scope}")
578 ELSE:
579 raise RuntimeError(
580 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
581 )
583 @property
584 def concurrency_limit(self) -> int:
585 """Current expected maximum concurrent stream-ordered workloads.
587 Reflects the driver-populated ``wqConfig.wqConcurrencyLimit`` field.
588 When first queried from a device, this matches the driver-reported
589 cap (typically ``CUDA_DEVICE_MAX_CONNECTIONS``). It can be updated
590 via :meth:`configure` with
591 :attr:`WorkqueueResourceOptions.concurrency_limit`.
592 """
593 IF CUDA_CORE_BUILD_MAJOR >= 13:
594 return self._wq_config_resource.wqConfig.wqConcurrencyLimit 1Y3
595 ELSE:
596 raise RuntimeError(
597 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
598 )
600 @property
601 def device(self) -> Device:
602 """The :class:`~cuda.core.Device` this workqueue resource is available on."""
603 IF CUDA_CORE_BUILD_MAJOR >= 13:
604 from cuda.core._device import Device # avoid circular import 13
605 return Device(int(self._wq_config_resource.wqConfig.device)) 13
606 ELSE:
607 raise RuntimeError(
608 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
609 )
611 def configure(self, options: WorkqueueResourceOptions) -> None:
612 """Configure the workqueue resource in place.
614 Parameters
615 ----------
616 options : :obj:`WorkqueueResourceOptions`
617 Configuration options (sharing scope, concurrency limit).
618 """
619 cdef WorkqueueResourceOptions opts = check_or_create_options( 1kXY2SOZ
620 WorkqueueResourceOptions, options, "Workqueue resource options"
621 )
622 _check_green_ctx_support() 1kXY2SOZ
623 _check_workqueue_support() 1kXY2SOZ
624 if opts.sharing_scope is None and opts.concurrency_limit is None: 1kXY2SOZ
625 return None 12
627 IF CUDA_CORE_BUILD_MAJOR >= 13:
628 if opts.concurrency_limit is not None: 1kXYSOZ
629 self._wq_config_resource.wqConfig.wqConcurrencyLimit = ( 1kXY
630 <unsigned int>opts.concurrency_limit 1kXY
631 )
632 if opts.sharing_scope == "device_ctx": 1kXYSOZ
633 self._wq_config_resource.wqConfig.sharingScope = ( 1S
634 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX
635 )
636 elif opts.sharing_scope == "green_ctx_balanced": 1kXYOZ
637 self._wq_config_resource.wqConfig.sharingScope = ( 1kXOZ
638 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED
639 )
640 ELSE:
641 raise RuntimeError(
642 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
643 )
646cdef class DeviceResources:
647 """Namespace for hardware resource queries.
649 When obtained via :obj:`Device.resources`, queries return full device
650 resources. When obtained via :obj:`Context.resources` or
651 :obj:`Stream.resources`, queries return the resources provisioned for
652 that context.
654 This class cannot be instantiated directly.
655 """
657 def __init__(self, *args, **kwargs) -> None:
658 raise RuntimeError( 17
659 "DeviceResources cannot be instantiated directly. "
660 "Use dev.resources or ctx.resources."
661 )
663 @staticmethod
664 cdef DeviceResources _init(int device_id):
665 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1ycdezwfghiajABCDEFGHIJKLMNPQRTUVW
666 self._device_id = device_id 1ycdezwfghiajABCDEFGHIJKLMNPQRTUVW
667 # _h_context is default empty — queries use cuDeviceGetDevResource
668 return self 1ycdezwfghiajABCDEFGHIJKLMNPQRTUVW
670 @staticmethod
671 cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id):
672 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1bxv
673 self._device_id = device_id 1bxv
674 self._h_context = h_context 1bxv
675 return self 1bxv
677 cdef inline int _query_sm(self, cydriver.CUdevResource* res) except?-1 nogil:
678 """Query SM resource from either device or context."""
679 cdef GreenCtxHandle h_green
680 if self._h_context: 1byxcvdezwfghiajABCDEFGHIJKLMN
681 h_green = get_context_green_ctx(self._h_context) 1bxv
682 if h_green: 1bxv
683 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1bxv
684 as_cu(h_green), res,
685 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
686 ))
687 else:
688 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
689 as_cu(self._h_context), res,
690 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
691 ))
692 else:
693 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1ycdezwfghiajABCDEFGHIJKLMN
694 <cydriver.CUdevice>(self._device_id), res,
695 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1ycdezwfghiajABCDEFGHIJKLMN
696 ))
697 return 0 1byxcvdezwfghiajABCDEFGHIJKLMN
699 @property
700 def sm(self) -> SMResource:
701 """Return the :obj:`SMResource` for this device or context."""
702 _check_green_ctx_support() 1byxcvdezwfghiajABCDEFGHIJKLMN
703 cdef cydriver.CUdevResource res
704 with nogil: 1byxcvdezwfghiajABCDEFGHIJKLMN
705 self._query_sm(&res) 1byxcvdezwfghiajABCDEFGHIJKLMN
706 return SMResource._from_dev_resource(res, self._device_id) 1byxcvdezwfghiajABCDEFGHIJKLMN
708 @property
709 def workqueue(self) -> WorkqueueResource:
710 """Return the :obj:`WorkqueueResource` for this device or context."""
711 _check_green_ctx_support() 1vwPQRTUVW
712 _check_workqueue_support() 1vwPQRTUVW
713 cdef cydriver.CUdevResource _wq_config
714 cdef cydriver.CUdevResource _wq
716 IF CUDA_CORE_BUILD_MAJOR >= 13:
717 cdef GreenCtxHandle h_green
718 if self._h_context: 1vwPQRTUVW
719 h_green = get_context_green_ctx(self._h_context) 1v
720 if h_green: 1v
721 # Green context query
722 with nogil: 1v
723 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1v
724 as_cu(h_green),
725 &_wq_config,
726 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
727 ))
728 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1v
729 as_cu(h_green),
730 &_wq,
731 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
732 ))
733 else:
734 # Primary context query
735 with nogil:
736 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
737 as_cu(self._h_context),
738 &_wq_config,
739 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
740 ))
741 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
742 as_cu(self._h_context),
743 &_wq,
744 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
745 ))
746 else:
747 # Device-level query
748 with nogil: 1wPQRTUVW
749 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1wPQRTUVW
750 <cydriver.CUdevice>(self._device_id),
751 &_wq_config,
752 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
753 ))
754 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1wPQRTUVW
755 <cydriver.CUdevice>(self._device_id),
756 &_wq,
757 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
758 ))
759 return WorkqueueResource._from_dev_resources(_wq_config, _wq) 1vwPQRTUVW
760 ELSE:
761 raise RuntimeError(
762 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
763 )