Coverage for cuda/core/_device_resources.pyx: 76.14%
306 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-29 01:38 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-29 01:38 +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: 1byxcvdenzkwfghiajABCDpEqFlGrHmIoJsKuLMNXPYQ2RSTOUZVW
46 return 0 1byxcvdenzkwfghiajABCDpEqFlGrHmIoJsKuLMNXPYQ2RSTOUZVW
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: 1bcdenkfghiajpqlrmosu10
163 if is_sequence(value): 1cdekfghiajpqlrsu1
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 1bcdenkfghiajpqlrmosu
176cdef inline int _resolve_group_count(SMResourceOptions options) except?-1:
177 cdef object count = options.count 1bcdenkfghiajpqlrmosu10
178 cdef int n_groups
179 cdef bint count_is_scalar
181 if count is None or isinstance(count, int): 1bcdenkfghiajpqlrmosu10
182 n_groups = 1 1cdekfghiajpqlrsu1
183 count_is_scalar = True 1cdekfghiajpqlrsu1
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( 1bcdenkfghiajpqlrmosu10
193 options.coscheduled_sm_count, 1bcdenkfghiajpqlrmosu10
194 "coscheduled_sm_count",
195 n_groups,
196 count_is_scalar,
197 )
198 _validate_split_field_length( 1bcdenkfghiajpqlrmosu
199 options.preferred_coscheduled_sm_count, 1bcdenkfghiajpqlrmosu
200 "preferred_coscheduled_sm_count",
201 n_groups,
202 count_is_scalar,
203 )
204 _validate_split_field_length( 1bcdenkfghiajpqlrmosu
205 options.backfill, 1bcdenkfghiajpqlrmosu
206 "backfill",
207 n_groups,
208 count_is_scalar,
209 )
210 return n_groups 1bcdenkfghiajpqlrmosu
213cdef inline object _broadcast_field(object value, int n_groups):
214 if is_sequence(value): 1bcdenkfghiajpqlrmosu
215 return list(value) 1bnmo
216 return [value] * n_groups 1bcdenkfghiajpqlrmosu
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: 1bcdenkfghiajpqlrmosu
222 return 0 1cdekfghiajpqls
223 if value < 0: 1bnrmou
224 raise ValueError(f"count must be non-negative, got {value}") 1u
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: 1bcdenkfghiajpqlrmosu
237 return _structured_split_checked == 1 1bcdenkfghijpqlrmosu
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
254 if options.coscheduled_sm_count is not None:
255 raise RuntimeError(
256 "SMResourceOptions.coscheduled_sm_count requires the CUDA 13.1 "
257 "structured SM split API"
258 )
259 if options.preferred_coscheduled_sm_count is not None:
260 raise RuntimeError(
261 "SMResourceOptions.preferred_coscheduled_sm_count requires the "
262 "CUDA 13.1 structured SM split API"
263 )
265 for value in counts[1:]:
266 if value != first:
267 raise RuntimeError(
268 "CUDA 12 SM splitting only supports homogeneous count values; "
269 "use CUDA 13.1 or newer for per-group counts"
270 )
272 cdef unsigned int min_count = _to_sm_count(first)
273 return n_groups, min_count
276IF CUDA_CORE_BUILD_MAJOR >= 13:
277 cdef inline int _fill_group_params(
278 cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params,
279 int n_groups,
280 SMResourceOptions options,
281 ) except?-1:
282 cdef list counts = _broadcast_field(options.count, n_groups) 1bcdenkfghiajpqlrmosu
283 cdef list coscheduled = _broadcast_field(options.coscheduled_sm_count, n_groups) 1bcdenkfghiajpqlrmosu
284 cdef list preferred = _broadcast_field(options.preferred_coscheduled_sm_count, n_groups) 1bcdenkfghiajpqlrmosu
285 cdef list backfills = _broadcast_field(options.backfill, n_groups) 1bcdenkfghiajpqlrmosu
286 cdef int i
288 for i in range(n_groups): 1bcdenkfghiajpqlrmosu
289 memset(¶ms[i], 0, sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)) 1bcdenkfghiajpqlrmosu
290 params[i].smCount = _to_sm_count(counts[i]) 1bcdenkfghiajpqlrmosu
291 if coscheduled[i] is not None: 1bcdenkfghiajpqlrmos
292 params[i].coscheduledSmCount = <unsigned int>(coscheduled[i])
293 if preferred[i] is not None: 1bcdenkfghiajpqlrmos
294 params[i].preferredCoscheduledSmCount = <unsigned int>(preferred[i])
295 params[i].flags = ( 1bcdenkfghiajpqlrmos
296 cydriver.CUdevSmResourceGroup_flags.CU_DEV_SM_RESOURCE_GROUP_BACKFILL
297 if backfills[i] else 0 1bcdenkfghiajpqlrmos
298 )
299 return 0 1bcdenkfghiajpqlrmos
302 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
303 cdef int n_groups = _resolve_group_count(options) 1bcdenkfghiajpqlrmosu
304 cdef cydriver.CUdevResource* result = NULL 1bcdenkfghiajpqlrmosu
305 cdef cydriver.CUdevResource remaining
306 cdef cydriver.CUdevResource synth
307 cdef cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params = NULL 1bcdenkfghiajpqlrmosu
308 cdef list groups = [] 1bcdenkfghiajpqlrmosu
309 cdef int i
311 params = <cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS*>malloc( 1bcdenkfghiajpqlrmosu
312 n_groups * sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)
313 )
314 if params == NULL: 1bcdenkfghiajpqlrmosu
315 raise MemoryError()
317 try: 1bcdenkfghiajpqlrmosu
318 _fill_group_params(params, n_groups, options) 1bcdenkfghiajpqlrmosu
320 if not dry_run: 1bcdenkfghiajpqlrmos
321 result = <cydriver.CUdevResource*>malloc( 1bcdenkfghiajpqlrmo
322 n_groups * sizeof(cydriver.CUdevResource)
323 )
324 if result == NULL: 1bcdenkfghiajpqlrmo
325 raise MemoryError()
327 memset(&remaining, 0, sizeof(cydriver.CUdevResource)) 1bcdenkfghiajpqlrmos
328 with nogil: 1bcdenkfghiajpqlrmos
329 HANDLE_RETURN(sm_resource_split( 1bcdenkfghiajpqlrmos
330 result,
331 <unsigned int>(n_groups),
332 &sm._resource,
333 &remaining,
334 0,
335 <void*>params,
336 ))
338 if result != NULL: 1bcdenkfghiajpqlrmos
339 for i in range(n_groups): 1bcdenkfghiajpqlrmo
340 groups.append(SMResource._from_split_resource(result[i], sm, True)) 1bcdenkfghiajpqlrmo
341 return groups, SMResource._from_split_resource(remaining, sm, True) 1bcdenkfghiajpqlrmo
343 for i in range(n_groups): 1ls
344 memset(&synth, 0, sizeof(cydriver.CUdevResource)) 1ls
345 synth.type = cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM 1ls
346 synth.sm.smCount = params[i].smCount 1ls
347 groups.append(SMResource._from_split_resource(synth, sm, False)) 1ls
348 return groups, SMResource._from_split_resource(remaining, sm, False) 1ls
349 finally:
350 if params != NULL: 1bcdenkfghiajpqlrmos
351 free(params) 1bcdenkfghiajpqlrmosu
352 if result != NULL: 1bcdenkfghiajpqlrmosu
353 free(result) 1bcdenkfghiajpqlrmo
354ELSE:
355 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
356 raise RuntimeError(
357 "SMResource.split() requires cuda.core to be built with CUDA 13.x bindings"
358 )
361cdef object _split_with_count_api(SMResource sm, SMResourceOptions options, bint dry_run):
362 cdef object request = _resolve_split_by_count_request(options)
363 cdef unsigned int nb_groups = <unsigned int>(request[0])
364 cdef unsigned int min_count = <unsigned int>(request[1])
365 cdef unsigned int actual_groups = nb_groups
366 cdef cydriver.CUdevResource* result = NULL
367 cdef cydriver.CUdevResource remaining
368 cdef list groups = []
369 cdef int i
371 result = <cydriver.CUdevResource*>malloc(nb_groups * sizeof(cydriver.CUdevResource))
372 if result == NULL:
373 raise MemoryError()
375 try:
376 memset(&remaining, 0, sizeof(cydriver.CUdevResource))
377 with nogil:
378 HANDLE_RETURN(cydriver.cuDevSmResourceSplitByCount(
379 result,
380 &actual_groups,
381 &sm._resource,
382 &remaining,
383 0,
384 min_count,
385 ))
387 for i in range(actual_groups):
388 if dry_run:
389 groups.append(SMResource._from_split_resource(result[i], sm, False))
390 else:
391 groups.append(SMResource._from_split_resource(result[i], sm, True))
392 if dry_run:
393 return groups, SMResource._from_split_resource(remaining, sm, False)
394 return groups, SMResource._from_split_resource(remaining, sm, True)
395 finally:
396 free(result)
399cdef inline unsigned int _sm_resource_granularity(int device_id) except? 0:
400 cdef int major
402 with nogil:
403 HANDLE_RETURN(cydriver.cuDeviceGetAttribute(
404 &major,
405 cydriver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
406 <cydriver.CUdevice>(device_id),
407 ))
408 if major >= 9:
409 return 8
410 return 2
413cdef inline unsigned int _fallback_if_zero(unsigned int value, unsigned int fallback) noexcept:
414 if value != 0: 1bcdenkfghiajpqlrmos
415 return value 1bcdenkfghiajpqlrmo
416 return fallback 1cdekfghiajpqlms
419cdef class SMResource:
420 """Represent an SM (streaming multiprocessor) resource partition.
422 Instances are returned by :obj:`DeviceResources.sm` or
423 :meth:`SMResource.split` and cannot be instantiated directly.
424 """
426 def __init__(self, *args, **kwargs):
427 raise RuntimeError( 17
428 "SMResource cannot be instantiated directly. "
429 "Use dev.resources.sm or SMResource.split()."
430 )
432 @staticmethod
433 cdef SMResource _from_dev_resource(cydriver.CUdevResource res, int device_id):
434 cdef SMResource self = SMResource.__new__(SMResource) 1byxcvdezwfghiajABCDEFGHIJKLMN
435 self._resource = res 1byxcvdezwfghiajABCDEFGHIJKLMN
436 self._sm_count = res.sm.smCount 1byxcvdezwfghiajABCDEFGHIJKLMN
437 IF CUDA_CORE_BUILD_MAJOR >= 13:
438 self._min_partition_size = res.sm.minSmPartitionSize 1byxcvdezwfghiajABCDEFGHIJKLMN
439 self._coscheduled_alignment = res.sm.smCoscheduledAlignment 1byxcvdezwfghiajABCDEFGHIJKLMN
440 self._flags = res.sm.flags 1byxcvdezwfghiajABCDEFGHIJKLMN
441 ELSE:
442 self._min_partition_size = _sm_resource_granularity(device_id)
443 self._coscheduled_alignment = self._min_partition_size
444 self._flags = 0
445 self._is_usable = True 1byxcvdezwfghiajABCDEFGHIJKLMN
446 return self 1byxcvdezwfghiajABCDEFGHIJKLMN
448 @staticmethod
449 cdef SMResource _from_split_resource(cydriver.CUdevResource res, SMResource parent, bint is_usable):
450 cdef SMResource self = SMResource.__new__(SMResource) 1bcdenkfghiajpqlrmos
451 self._resource = res 1bcdenkfghiajpqlrmos
452 self._sm_count = res.sm.smCount 1bcdenkfghiajpqlrmos
453 IF CUDA_CORE_BUILD_MAJOR >= 13:
454 self._min_partition_size = _fallback_if_zero( 1bcdenkfghiajpqlrmos
455 res.sm.minSmPartitionSize,
456 parent._min_partition_size,
457 )
458 self._coscheduled_alignment = _fallback_if_zero( 1bcdenkfghiajpqlrmos
459 res.sm.smCoscheduledAlignment,
460 parent._coscheduled_alignment,
461 )
462 self._flags = res.sm.flags 1bcdenkfghiajpqlrmos
463 ELSE:
464 self._min_partition_size = parent._min_partition_size
465 self._coscheduled_alignment = parent._coscheduled_alignment
466 self._flags = parent._flags
467 self._is_usable = is_usable 1bcdenkfghiajpqlrmos
468 return self 1bcdenkfghiajpqlrmos
470 @property
471 def handle(self) -> int:
472 """Return the address of the underlying ``CUdevResource`` struct."""
473 return <intptr_t>(&self._resource) 16
475 @property
476 def sm_count(self) -> int:
477 """Total SMs available in this resource."""
478 return self._sm_count 1bxvn6pqlrmo
480 @property
481 def min_partition_size(self) -> int:
482 """Minimum SM count required to create a partition."""
483 return self._min_partition_size 1bn96prmo10
485 @property
486 def coscheduled_alignment(self) -> int:
487 """Number of SMs guaranteed to be co-scheduled."""
488 return self._coscheduled_alignment 196q
490 @property
491 def flags(self) -> int:
492 """Raw flags from the underlying SM resource."""
493 return self._flags 16
495 def split(
496 self,
497 options: SMResourceOptions,
498 *,
499 bint dry_run=False
500 ) -> tuple[list[SMResource], SMResource]:
501 """Split this SM resource into groups and a remainder.
503 Parameters
504 ----------
505 options : :obj:`SMResourceOptions`
506 Split configuration (count, co-scheduling constraints).
507 dry_run : bool, optional
508 If ``True``, return filled-in metadata without creating
509 usable resource objects. (Default to ``False``)
511 Returns
512 -------
513 tuple[list[:obj:`SMResource`], :obj:`SMResource`]
514 ``(groups, remainder)`` where each group holds a disjoint
515 SM partition and *remainder* holds any unassigned SMs.
516 """
517 cdef SMResourceOptions opts = check_or_create_options( 1bcdenkfghiajpqlrmosu10
518 SMResourceOptions, options, "SM resource options"
519 )
520 _resolve_group_count(opts) 1bcdenkfghiajpqlrmosu10
521 _check_green_ctx_support() 1bcdenkfghiajpqlrmosu
522 if _can_use_structured_sm_split(): 1bcdenkfghiajpqlrmosu
523 return _split_with_general_api(self, opts, dry_run) 1bcdenkfghiajpqlrmosu
524 # SplitByCount requires the same 12.4+ as green ctx support (already checked above)
525 return _split_with_count_api(self, opts, dry_run)
528cdef class WorkqueueResource:
529 """Represent a workqueue resource for a device or green context.
531 Merges ``CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG`` and
532 ``CU_DEV_RESOURCE_TYPE_WORKQUEUE`` under one user-facing type.
533 Instances are returned by :obj:`DeviceResources.workqueue` and
534 cannot be instantiated directly.
535 """
537 def __init__(self, *args, **kwargs) -> None:
538 raise RuntimeError( 17
539 "WorkqueueResource cannot be instantiated directly. "
540 "Use dev.resources.workqueue."
541 )
543 @staticmethod
544 cdef WorkqueueResource _from_dev_resources(
545 cydriver.CUdevResource wq_config,
546 cydriver.CUdevResource wq,
547 ):
548 cdef WorkqueueResource self = WorkqueueResource.__new__(WorkqueueResource) 1vwPQRTUVW
549 self._wq_config_resource = wq_config 1vwPQRTUVW
550 self._wq_resource = wq 1vwPQRTUVW
551 return self 1vwPQRTUVW
553 @property
554 def handle(self) -> int:
555 """Return the address of the underlying config ``CUdevResource`` struct."""
556 return <intptr_t>(&self._wq_config_resource) 1v3
558 @property
559 def sharing_scope(self) -> WorkqueueSharingScopeType:
560 """Current sharing scope of this workqueue resource.
562 Returns the :class:`~cuda.core.typing.WorkqueueSharingScopeType`
563 member corresponding to the driver-populated
564 ``wqConfig.sharingScope`` field. It can be updated via
565 :meth:`configure` with
566 :attr:`WorkqueueResourceOptions.sharing_scope`.
567 """
568 IF CUDA_CORE_BUILD_MAJOR >= 13:
569 from cuda.core.typing import WorkqueueSharingScopeType 1SO3
570 cdef object scope = self._wq_config_resource.wqConfig.sharingScope 1SO3
571 if scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX: 1SO3
572 return WorkqueueSharingScopeType.DEVICE_CTX 1S3
573 elif scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED: 1O
574 return WorkqueueSharingScopeType.GREEN_CTX_BALANCED 1O
575 raise RuntimeError(f"Unknown sharing scope enum value: {scope}")
576 ELSE:
577 raise RuntimeError(
578 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
579 )
581 @property
582 def concurrency_limit(self) -> int:
583 """Current expected maximum concurrent stream-ordered workloads.
585 Reflects the driver-populated ``wqConfig.wqConcurrencyLimit`` field.
586 When first queried from a device, this matches the driver-reported
587 cap (typically ``CUDA_DEVICE_MAX_CONNECTIONS``). It can be updated
588 via :meth:`configure` with
589 :attr:`WorkqueueResourceOptions.concurrency_limit`.
590 """
591 IF CUDA_CORE_BUILD_MAJOR >= 13:
592 return self._wq_config_resource.wqConfig.wqConcurrencyLimit 1Y3
593 ELSE:
594 raise RuntimeError(
595 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
596 )
598 @property
599 def device(self) -> Device:
600 """The :class:`~cuda.core.Device` this workqueue resource is available on."""
601 IF CUDA_CORE_BUILD_MAJOR >= 13:
602 from cuda.core._device import Device # avoid circular import 13
603 return Device(int(self._wq_config_resource.wqConfig.device)) 13
604 ELSE:
605 raise RuntimeError(
606 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
607 )
609 def configure(self, options: WorkqueueResourceOptions) -> None:
610 """Configure the workqueue resource in place.
612 Parameters
613 ----------
614 options : :obj:`WorkqueueResourceOptions`
615 Configuration options (sharing scope, concurrency limit).
616 """
617 cdef WorkqueueResourceOptions opts = check_or_create_options( 1kXY2SOZ
618 WorkqueueResourceOptions, options, "Workqueue resource options"
619 )
620 _check_green_ctx_support() 1kXY2SOZ
621 _check_workqueue_support() 1kXY2SOZ
622 if opts.sharing_scope is None and opts.concurrency_limit is None: 1kXY2SOZ
623 return None 12
625 IF CUDA_CORE_BUILD_MAJOR >= 13:
626 if opts.concurrency_limit is not None: 1kXYSOZ
627 self._wq_config_resource.wqConfig.wqConcurrencyLimit = ( 1kXY
628 <unsigned int>opts.concurrency_limit 1kXY
629 )
630 if opts.sharing_scope == "device_ctx": 1kXYSOZ
631 self._wq_config_resource.wqConfig.sharingScope = ( 1S
632 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX
633 )
634 elif opts.sharing_scope == "green_ctx_balanced": 1kXYOZ
635 self._wq_config_resource.wqConfig.sharingScope = ( 1kXOZ
636 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED
637 )
638 ELSE:
639 raise RuntimeError(
640 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
641 )
644cdef class DeviceResources:
645 """Namespace for hardware resource queries.
647 When obtained via :obj:`Device.resources`, queries return full device
648 resources. When obtained via :obj:`Context.resources` or
649 :obj:`Stream.resources`, queries return the resources provisioned for
650 that context.
652 This class cannot be instantiated directly.
653 """
655 def __init__(self, *args, **kwargs) -> None:
656 raise RuntimeError( 17
657 "DeviceResources cannot be instantiated directly. "
658 "Use dev.resources or ctx.resources."
659 )
661 @staticmethod
662 cdef DeviceResources _init(int device_id):
663 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1ycdezwfghiajABCDEFGHIJKLMNPQRTUVW
664 self._device_id = device_id 1ycdezwfghiajABCDEFGHIJKLMNPQRTUVW
665 # _h_context is default empty — queries use cuDeviceGetDevResource
666 return self 1ycdezwfghiajABCDEFGHIJKLMNPQRTUVW
668 @staticmethod
669 cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id):
670 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1bxv
671 self._device_id = device_id 1bxv
672 self._h_context = h_context 1bxv
673 return self 1bxv
675 cdef inline int _query_sm(self, cydriver.CUdevResource* res) except?-1 nogil:
676 """Query SM resource from either device or context."""
677 cdef GreenCtxHandle h_green
678 if self._h_context: 1byxcvdezwfghiajABCDEFGHIJKLMN
679 h_green = get_context_green_ctx(self._h_context) 1bxv
680 if h_green: 1bxv
681 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1bxv
682 as_cu(h_green), res,
683 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
684 ))
685 else:
686 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
687 as_cu(self._h_context), res,
688 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
689 ))
690 else:
691 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1ycdezwfghiajABCDEFGHIJKLMN
692 <cydriver.CUdevice>(self._device_id), res,
693 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1ycdezwfghiajABCDEFGHIJKLMN
694 ))
695 return 0 1byxcvdezwfghiajABCDEFGHIJKLMN
697 @property
698 def sm(self) -> SMResource:
699 """Return the :obj:`SMResource` for this device or context."""
700 _check_green_ctx_support() 1byxcvdezwfghiajABCDEFGHIJKLMN
701 cdef cydriver.CUdevResource res
702 with nogil: 1byxcvdezwfghiajABCDEFGHIJKLMN
703 self._query_sm(&res) 1byxcvdezwfghiajABCDEFGHIJKLMN
704 return SMResource._from_dev_resource(res, self._device_id) 1byxcvdezwfghiajABCDEFGHIJKLMN
706 @property
707 def workqueue(self) -> WorkqueueResource:
708 """Return the :obj:`WorkqueueResource` for this device or context."""
709 _check_green_ctx_support() 1vwPQRTUVW
710 _check_workqueue_support() 1vwPQRTUVW
711 cdef cydriver.CUdevResource _wq_config
712 cdef cydriver.CUdevResource _wq
714 IF CUDA_CORE_BUILD_MAJOR >= 13:
715 cdef GreenCtxHandle h_green
716 if self._h_context: 1vwPQRTUVW
717 h_green = get_context_green_ctx(self._h_context) 1v
718 if h_green: 1v
719 # Green context query
720 with nogil: 1v
721 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1v
722 as_cu(h_green),
723 &_wq_config,
724 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
725 ))
726 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1v
727 as_cu(h_green),
728 &_wq,
729 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
730 ))
731 else:
732 # Primary context query
733 with nogil:
734 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
735 as_cu(self._h_context),
736 &_wq_config,
737 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
738 ))
739 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
740 as_cu(self._h_context),
741 &_wq,
742 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
743 ))
744 else:
745 # Device-level query
746 with nogil: 1wPQRTUVW
747 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1wPQRTUVW
748 <cydriver.CUdevice>(self._device_id),
749 &_wq_config,
750 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
751 ))
752 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1wPQRTUVW
753 <cydriver.CUdevice>(self._device_id),
754 &_wq,
755 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
756 ))
757 return WorkqueueResource._from_dev_resources(_wq_config, _wq) 1vwPQRTUVW
758 ELSE:
759 raise RuntimeError(
760 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
761 )