Coverage for cuda/core/_device_resources.pyx: 75.00%
284 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-13 01:38 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-13 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
10from libc.stdint cimport intptr_t
11from libc.stdlib cimport free, malloc
12from libc.string cimport memset
14from cuda.bindings cimport cydriver
15from cuda.core._resource_handles cimport ContextHandle, GreenCtxHandle, as_cu, get_context_green_ctx
16from cuda.core._utils.cuda_utils cimport check_or_create_options, HANDLE_RETURN
17from cuda.core._utils.cuda_utils import is_sequence
18from cuda.core._utils.version cimport cy_binding_version, cy_driver_version
21__all__ = [
22 "DeviceResources",
23 "SMResource",
24 "SMResourceOptions",
25 "WorkqueueResource",
26 "WorkqueueResourceOptions",
27]
30# Module-level cached version checks (trinary: 0=unchecked, 1=supported, -1=unsupported)
31cdef int _green_ctx_checked = 0
32cdef int _workqueue_checked = 0
33cdef str _green_ctx_err_msg = ""
34cdef str _workqueue_err_msg = ""
37cdef inline int _check_green_ctx_support() except?-1:
38 global _green_ctx_checked, _green_ctx_err_msg
39 if _green_ctx_checked == 1: 1azybvcdmArwefghijBCDEoFpGkHqIlJnKsLtMNOUxWPVQR
40 return 0 1azybvcdmArwefghijBCDEoFpGkHqIlJnKsLtMNOUWPVQR
41 if _green_ctx_checked == -1: 1x
42 raise RuntimeError(_green_ctx_err_msg)
43 cdef tuple drv = cy_driver_version() 1x
44 cdef tuple bind = cy_binding_version() 1x
45 if drv < (12, 4, 0): 1x
46 _green_ctx_err_msg = (
47 "Green context support requires CUDA driver 12.4 or newer "
48 f"(current driver: {'.'.join(map(str, drv))})"
49 )
50 _green_ctx_checked = -1
51 raise RuntimeError(_green_ctx_err_msg)
52 if bind < (12, 4, 0): 1x
53 _green_ctx_err_msg = (
54 "Green context support requires cuda.bindings 12.4 or newer "
55 f"(current bindings: {'.'.join(map(str, bind))})"
56 )
57 _green_ctx_checked = -1
58 raise RuntimeError(_green_ctx_err_msg)
59 _green_ctx_checked = 1 1x
60 return 0 1x
63cdef inline int _check_workqueue_support() except?-1:
64 global _workqueue_checked, _workqueue_err_msg
65 if _workqueue_checked == 1: 1vwUxWPVQR
66 return 0 1vwUWPVQR
67 if _workqueue_checked == -1: 1x
68 raise RuntimeError(_workqueue_err_msg)
69 cdef tuple drv = cy_driver_version() 1x
70 cdef tuple bind = cy_binding_version() 1x
71 if drv < (13, 1, 0): 1x
72 _workqueue_err_msg = (
73 "WorkqueueResource requires CUDA driver 13.1 or newer "
74 f"(current driver: {'.'.join(map(str, drv))})"
75 )
76 _workqueue_checked = -1
77 raise RuntimeError(_workqueue_err_msg)
78 if bind < (13, 1, 0): 1x
79 _workqueue_err_msg = (
80 "WorkqueueResource requires cuda.bindings 13.1 or newer "
81 f"(current bindings: {'.'.join(map(str, bind))})"
82 )
83 _workqueue_checked = -1
84 raise RuntimeError(_workqueue_err_msg)
85 _workqueue_checked = 1 1x
86 return 0 1x
89@dataclass
90cdef class SMResourceOptions:
91 """Customizable :obj:`SMResource.split` options.
93 Each field accepts a scalar (for a single group) or a ``Sequence``
94 (for multiple groups). ``count`` drives the number of groups; other
95 ``Sequence`` fields must match its length.
97 Attributes
98 ----------
99 count : int or Sequence[int], optional
100 Requested SM count per group. ``None`` means discovery mode
101 (auto-detect). (Default to ``None``)
102 coscheduled_sm_count : int or Sequence[int], optional
103 Minimum number of SMs guaranteed to be co-scheduled in each
104 group. (Default to ``None``)
105 preferred_coscheduled_sm_count : int or Sequence[int], optional
106 Preferred co-scheduled SM count; the driver tries to satisfy
107 this but may fall back to ``coscheduled_sm_count``.
108 (Default to ``None``)
109 backfill : bool or Sequence[bool], optional
110 If ``True``, allow the driver to relax the co-scheduling
111 constraint when assigning SMs. This enables requesting
112 arbitrary aligned SM counts that the driver would otherwise
113 reject due to hardware topology constraints.
114 (Default to ``False``)
115 """
117 count: int | SequenceABC[int] | None = None
118 coscheduled_sm_count: int | SequenceABC[int] | None = None
119 preferred_coscheduled_sm_count: int | SequenceABC[int] | None = None
120 backfill: bool | SequenceABC[bool] = False
123@dataclass
124cdef class WorkqueueResourceOptions:
125 """Customizable :obj:`WorkqueueResource.configure` options.
127 Attributes
128 ----------
129 sharing_scope : str, optional
130 Workqueue sharing scope. Accepted values: ``"device_ctx"``
131 or ``"green_ctx_balanced"``. (Default to ``None``)
132 """
134 sharing_scope: str | None = None
137cdef inline int _validate_split_field_length(
138 object value, str field_name, int n_groups, bint count_is_scalar
139) except?-1:
140 if count_is_scalar: 1abcdmrefghijopkqlnstTS
141 if is_sequence(value): 1bcdrefghijopkqstT
142 raise ValueError( 1T
143 f"{field_name} is a Sequence but count is scalar; " 1T
144 "count must be a Sequence to specify multiple groups"
145 )
146 elif is_sequence(value) and len(value) != n_groups: 1amlnS
147 raise ValueError( 1S
148 f"{field_name} has length {len(value)}, expected {n_groups} " 1S
149 "(must match count)"
150 )
151 return 0 1abcdmrefghijopkqlnst
154cdef inline int _resolve_group_count(SMResourceOptions options) except?-1:
155 cdef object count = options.count 1abcdmrefghijopkqlnstTS
156 cdef int n_groups
157 cdef bint count_is_scalar
159 if count is None or isinstance(count, int): 1abcdmrefghijopkqlnstTS
160 n_groups = 1 1bcdrefghijopkqstT
161 count_is_scalar = True 1bcdrefghijopkqstT
162 elif is_sequence(count): 1amlnS
163 n_groups = len(count) 1amlnS
164 if n_groups == 0: 1amlnS
165 raise ValueError("count sequence must not be empty")
166 count_is_scalar = False 1amlnS
167 else:
168 raise TypeError(f"count must be int, Sequence, or None, got {type(count)}")
170 _validate_split_field_length( 1abcdmrefghijopkqlnstTS
171 options.coscheduled_sm_count, 1abcdmrefghijopkqlnstTS
172 "coscheduled_sm_count",
173 n_groups,
174 count_is_scalar,
175 )
176 _validate_split_field_length( 1abcdmrefghijopkqlnst
177 options.preferred_coscheduled_sm_count, 1abcdmrefghijopkqlnst
178 "preferred_coscheduled_sm_count",
179 n_groups,
180 count_is_scalar,
181 )
182 _validate_split_field_length( 1abcdmrefghijopkqlnst
183 options.backfill, 1abcdmrefghijopkqlnst
184 "backfill",
185 n_groups,
186 count_is_scalar,
187 )
188 return n_groups 1abcdmrefghijopkqlnst
191cdef inline object _broadcast_field(object value, int n_groups):
192 if is_sequence(value): 1abcdmrefghijopkqlnst
193 return list(value) 1amln
194 return [value] * n_groups 1abcdmrefghijopkqlnst
197cdef inline unsigned int _to_sm_count(object value) except? 0:
198 """Convert a count value to unsigned int. None maps to 0 (discovery)."""
199 if value is None: 1abcdmrefghijopkqlnst
200 return 0 1bcdrefghijopks
201 if value < 0: 1amqlnt
202 raise ValueError(f"count must be non-negative, got {value}") 1t
203 return <unsigned int>(value) 1amqln
206IF CUDA_CORE_BUILD_MAJOR >= 13:
207 from cuda.core._resource_handles cimport sm_resource_split, has_sm_resource_split
209cdef int _structured_split_checked = 0
211cdef inline bint _can_use_structured_sm_split():
212 """Check if cuDevSmResourceSplit (13.1+) is available. Cached."""
213 global _structured_split_checked
214 if _structured_split_checked != 0: 1abcdmrefghijopkqlnst
215 return _structured_split_checked == 1 1acdmrefghijopkqlnst
216 IF CUDA_CORE_BUILD_MAJOR >= 13:
217 if (has_sm_resource_split() 1b
218 and cy_driver_version() >= (13, 1, 0) 1b
219 and cy_binding_version() >= (13, 1, 0)): 1b
220 _structured_split_checked = 1 1b
221 return True 1b
222 _structured_split_checked = -1
223 return False
226cdef object _resolve_split_by_count_request(SMResourceOptions options):
227 cdef int n_groups = _resolve_group_count(options)
228 cdef list counts = _broadcast_field(options.count, n_groups)
229 cdef object first = counts[0]
230 cdef object value
231 cdef unsigned int min_count
233 if options.coscheduled_sm_count is not None:
234 raise RuntimeError(
235 "SMResourceOptions.coscheduled_sm_count requires the CUDA 13.1 "
236 "structured SM split API"
237 )
238 if options.preferred_coscheduled_sm_count is not None:
239 raise RuntimeError(
240 "SMResourceOptions.preferred_coscheduled_sm_count requires the "
241 "CUDA 13.1 structured SM split API"
242 )
244 for value in counts[1:]:
245 if value != first:
246 raise RuntimeError(
247 "CUDA 12 SM splitting only supports homogeneous count values; "
248 "use CUDA 13.1 or newer for per-group counts"
249 )
251 min_count = _to_sm_count(first)
252 return n_groups, min_count
255IF CUDA_CORE_BUILD_MAJOR >= 13:
256 cdef inline int _fill_group_params(
257 cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params,
258 int n_groups,
259 SMResourceOptions options,
260 ) except?-1:
261 cdef list counts = _broadcast_field(options.count, n_groups) 1abcdmrefghijopkqlnst
262 cdef list coscheduled = _broadcast_field(options.coscheduled_sm_count, n_groups) 1abcdmrefghijopkqlnst
263 cdef list preferred = _broadcast_field(options.preferred_coscheduled_sm_count, n_groups) 1abcdmrefghijopkqlnst
264 cdef list backfills = _broadcast_field(options.backfill, n_groups) 1abcdmrefghijopkqlnst
265 cdef int i
267 for i in range(n_groups): 1abcdmrefghijopkqlnst
268 memset(¶ms[i], 0, sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)) 1abcdmrefghijopkqlnst
269 params[i].smCount = _to_sm_count(counts[i]) 1abcdmrefghijopkqlnst
270 if coscheduled[i] is not None: 1abcdmrefghijopkqlns
271 params[i].coscheduledSmCount = <unsigned int>(coscheduled[i])
272 if preferred[i] is not None: 1abcdmrefghijopkqlns
273 params[i].preferredCoscheduledSmCount = <unsigned int>(preferred[i])
274 params[i].flags = ( 1abcdmrefghijopkqlns
275 cydriver.CUdevSmResourceGroup_flags.CU_DEV_SM_RESOURCE_GROUP_BACKFILL
276 if backfills[i] else 0 1abcdmrefghijopkqlns
277 )
278 return 0 1abcdmrefghijopkqlns
281 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
282 cdef int n_groups = _resolve_group_count(options) 1abcdmrefghijopkqlnst
283 cdef cydriver.CUdevResource* result = NULL 1abcdmrefghijopkqlnst
284 cdef cydriver.CUdevResource remaining
285 cdef cydriver.CUdevResource synth
286 cdef cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params = NULL 1abcdmrefghijopkqlnst
287 cdef list groups = [] 1abcdmrefghijopkqlnst
288 cdef int i
290 params = <cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS*>malloc( 1abcdmrefghijopkqlnst
291 n_groups * sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)
292 )
293 if params == NULL: 1abcdmrefghijopkqlnst
294 raise MemoryError()
296 try: 1abcdmrefghijopkqlnst
297 _fill_group_params(params, n_groups, options) 1abcdmrefghijopkqlnst
299 if not dry_run: 1abcdmrefghijopkqlns
300 result = <cydriver.CUdevResource*>malloc( 1abcdmrefghijopkqln
301 n_groups * sizeof(cydriver.CUdevResource)
302 )
303 if result == NULL: 1abcdmrefghijopkqln
304 raise MemoryError()
306 memset(&remaining, 0, sizeof(cydriver.CUdevResource)) 1abcdmrefghijopkqlns
307 with nogil: 1abcdmrefghijopkqlns
308 HANDLE_RETURN(sm_resource_split( 1abcdmrefghijopkqlns
309 result,
310 <unsigned int>(n_groups),
311 &sm._resource,
312 &remaining,
313 0,
314 <void*>params,
315 ))
317 if result != NULL: 1abcdmrefghijopkqlns
318 for i in range(n_groups): 1abcdmrefghijopkqln
319 groups.append(SMResource._from_split_resource(result[i], sm, True)) 1abcdmrefghijopkqln
320 return groups, SMResource._from_split_resource(remaining, sm, True) 1abcdmrefghijopkqln
322 for i in range(n_groups): 1ks
323 memset(&synth, 0, sizeof(cydriver.CUdevResource)) 1ks
324 synth.type = cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM 1ks
325 synth.sm.smCount = params[i].smCount 1ks
326 groups.append(SMResource._from_split_resource(synth, sm, False)) 1ks
327 return groups, SMResource._from_split_resource(remaining, sm, False) 1ks
328 finally:
329 if params != NULL: 1abcdmrefghijopkqlns
330 free(params) 1abcdmrefghijopkqlnst
331 if result != NULL: 1abcdmrefghijopkqlnst
332 free(result) 1abcdmrefghijopkqln
333ELSE:
334 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run):
335 raise RuntimeError(
336 "SMResource.split() requires cuda.core to be built with CUDA 13.x bindings"
337 )
340cdef object _split_with_count_api(SMResource sm, SMResourceOptions options, bint dry_run):
341 cdef object request = _resolve_split_by_count_request(options)
342 cdef unsigned int nb_groups = <unsigned int>(request[0])
343 cdef unsigned int min_count = <unsigned int>(request[1])
344 cdef unsigned int actual_groups = nb_groups
345 cdef cydriver.CUdevResource* result = NULL
346 cdef cydriver.CUdevResource remaining
347 cdef list groups = []
348 cdef int i
350 result = <cydriver.CUdevResource*>malloc(nb_groups * sizeof(cydriver.CUdevResource))
351 if result == NULL:
352 raise MemoryError()
354 try:
355 memset(&remaining, 0, sizeof(cydriver.CUdevResource))
356 with nogil:
357 HANDLE_RETURN(cydriver.cuDevSmResourceSplitByCount(
358 result,
359 &actual_groups,
360 &sm._resource,
361 &remaining,
362 0,
363 min_count,
364 ))
366 for i in range(actual_groups):
367 if dry_run:
368 groups.append(SMResource._from_split_resource(result[i], sm, False))
369 else:
370 groups.append(SMResource._from_split_resource(result[i], sm, True))
371 if dry_run:
372 return groups, SMResource._from_split_resource(remaining, sm, False)
373 return groups, SMResource._from_split_resource(remaining, sm, True)
374 finally:
375 free(result)
378cdef inline unsigned int _sm_resource_granularity(int device_id) except? 0:
379 cdef int major
381 with nogil:
382 HANDLE_RETURN(cydriver.cuDeviceGetAttribute(
383 &major,
384 cydriver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
385 <cydriver.CUdevice>(device_id),
386 ))
387 if major >= 9:
388 return 8
389 return 2
392cdef inline unsigned int _fallback_if_zero(unsigned int value, unsigned int fallback) noexcept:
393 if value != 0: 1abcdmrefghijopkqlns
394 return value 1abcdmrefghijopkqln
395 return fallback 1bcdrefghijopkls
398cdef class SMResource:
399 """Represent an SM (streaming multiprocessor) resource partition.
401 Instances are returned by :obj:`DeviceResources.sm` or
402 :meth:`SMResource.split` and cannot be instantiated directly.
403 """
405 def __init__(self, *args, **kwargs):
406 raise RuntimeError( 1Y
407 "SMResource cannot be instantiated directly. "
408 "Use dev.resources.sm or SMResource.split()."
409 )
411 @staticmethod
412 cdef SMResource _from_dev_resource(cydriver.CUdevResource res, int device_id):
413 cdef SMResource self = SMResource.__new__(SMResource) 1azybvcdAwefghijBCDEFGHIJKLMNO
414 self._resource = res 1azybvcdAwefghijBCDEFGHIJKLMNO
415 self._sm_count = res.sm.smCount 1azybvcdAwefghijBCDEFGHIJKLMNO
416 IF CUDA_CORE_BUILD_MAJOR >= 13:
417 self._min_partition_size = res.sm.minSmPartitionSize 1azybvcdAwefghijBCDEFGHIJKLMNO
418 self._coscheduled_alignment = res.sm.smCoscheduledAlignment 1azybvcdAwefghijBCDEFGHIJKLMNO
419 self._flags = res.sm.flags 1azybvcdAwefghijBCDEFGHIJKLMNO
420 ELSE:
421 self._min_partition_size = _sm_resource_granularity(device_id)
422 self._coscheduled_alignment = self._min_partition_size
423 self._flags = 0
424 self._is_usable = True 1azybvcdAwefghijBCDEFGHIJKLMNO
425 return self 1azybvcdAwefghijBCDEFGHIJKLMNO
427 @staticmethod
428 cdef SMResource _from_split_resource(cydriver.CUdevResource res, SMResource parent, bint is_usable):
429 cdef SMResource self = SMResource.__new__(SMResource) 1abcdmrefghijopkqlns
430 self._resource = res 1abcdmrefghijopkqlns
431 self._sm_count = res.sm.smCount 1abcdmrefghijopkqlns
432 IF CUDA_CORE_BUILD_MAJOR >= 13:
433 self._min_partition_size = _fallback_if_zero( 1abcdmrefghijopkqlns
434 res.sm.minSmPartitionSize,
435 parent._min_partition_size,
436 )
437 self._coscheduled_alignment = _fallback_if_zero( 1abcdmrefghijopkqlns
438 res.sm.smCoscheduledAlignment,
439 parent._coscheduled_alignment,
440 )
441 self._flags = res.sm.flags 1abcdmrefghijopkqlns
442 ELSE:
443 self._min_partition_size = parent._min_partition_size
444 self._coscheduled_alignment = parent._coscheduled_alignment
445 self._flags = parent._flags
446 self._is_usable = is_usable 1abcdmrefghijopkqlns
447 return self 1abcdmrefghijopkqlns
449 @property
450 def handle(self) -> int:
451 """Return the address of the underlying ``CUdevResource`` struct."""
452 return <intptr_t>(&self._resource) 1X
454 @property
455 def sm_count(self) -> int:
456 """Total SMs available in this resource."""
457 return self._sm_count 1ayvmXopkqln
459 @property
460 def min_partition_size(self) -> int:
461 """Minimum SM count required to create a partition."""
462 return self._min_partition_size 1amZXoqlnTS
464 @property
465 def coscheduled_alignment(self) -> int:
466 """Number of SMs guaranteed to be co-scheduled."""
467 return self._coscheduled_alignment 1ZXp
469 @property
470 def flags(self) -> int:
471 """Raw flags from the underlying SM resource."""
472 return self._flags 1X
474 def split(
475 self,
476 options: SMResourceOptions,
477 *,
478 bint dry_run=False
479 ) -> tuple[list[SMResource], SMResource]:
480 """Split this SM resource into groups and a remainder.
482 Parameters
483 ----------
484 options : :obj:`SMResourceOptions`
485 Split configuration (count, co-scheduling constraints).
486 dry_run : bool, optional
487 If ``True``, return filled-in metadata without creating
488 usable resource objects. (Default to ``False``)
490 Returns
491 -------
492 tuple[list[:obj:`SMResource`], :obj:`SMResource`]
493 ``(groups, remainder)`` where each group holds a disjoint
494 SM partition and *remainder* holds any unassigned SMs.
495 """
496 cdef SMResourceOptions opts = check_or_create_options( 1abcdmrefghijopkqlnstTS
497 SMResourceOptions, options, "SM resource options"
498 )
499 _resolve_group_count(opts) 1abcdmrefghijopkqlnstTS
500 _check_green_ctx_support() 1abcdmrefghijopkqlnst
501 if _can_use_structured_sm_split(): 1abcdmrefghijopkqlnst
502 return _split_with_general_api(self, opts, dry_run) 1abcdmrefghijopkqlnst
503 # SplitByCount requires the same 12.4+ as green ctx support (already checked above)
504 return _split_with_count_api(self, opts, dry_run)
507cdef class WorkqueueResource:
508 """Represent a workqueue resource for a device or green context.
510 Merges ``CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG`` and
511 ``CU_DEV_RESOURCE_TYPE_WORKQUEUE`` under one user-facing type.
512 Instances are returned by :obj:`DeviceResources.workqueue` and
513 cannot be instantiated directly.
514 """
516 def __init__(self, *args, **kwargs) -> None:
517 raise RuntimeError( 1Y
518 "WorkqueueResource cannot be instantiated directly. "
519 "Use dev.resources.workqueue."
520 )
522 @staticmethod
523 cdef WorkqueueResource _from_dev_resources(
524 cydriver.CUdevResource wq_config,
525 cydriver.CUdevResource wq,
526 ):
527 cdef WorkqueueResource self = WorkqueueResource.__new__(WorkqueueResource) 1vwxPQR
528 self._wq_config_resource = wq_config 1vwxPQR
529 self._wq_resource = wq 1vwxPQR
530 return self 1vwxPQR
532 @property
533 def handle(self) -> int:
534 """Return the address of the underlying config ``CUdevResource`` struct."""
535 return <intptr_t>(&self._wq_config_resource) 1v0
537 def configure(self, options: WorkqueueResourceOptions) -> None:
538 """Configure the workqueue resource in place.
540 Parameters
541 ----------
542 options : :obj:`WorkqueueResourceOptions`
543 Configuration options (sharing scope, etc.).
544 """
545 cdef WorkqueueResourceOptions opts = check_or_create_options( 1UWV
546 WorkqueueResourceOptions, options, "Workqueue resource options"
547 )
548 _check_green_ctx_support() 1UWV
549 _check_workqueue_support() 1UWV
550 if opts.sharing_scope is None: 1UWV
551 return None 1W
553 IF CUDA_CORE_BUILD_MAJOR >= 13:
554 if opts.sharing_scope == "device_ctx": 1UV
555 self._wq_config_resource.wqConfig.sharingScope = (
556 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX
557 )
558 elif opts.sharing_scope == "green_ctx_balanced": 1UV
559 self._wq_config_resource.wqConfig.sharingScope = ( 1V
560 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED
561 )
562 else:
563 raise ValueError( 1U
564 f"Unknown sharing_scope: {opts.sharing_scope!r}. " 1U
565 "Expected 'device_ctx' or 'green_ctx_balanced'."
566 )
567 ELSE:
568 raise RuntimeError(
569 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
570 )
573cdef class DeviceResources:
574 """Namespace for hardware resource queries.
576 When obtained via :obj:`Device.resources`, queries return full device
577 resources. When obtained via :obj:`Context.resources` or
578 :obj:`Stream.resources`, queries return the resources provisioned for
579 that context.
581 This class cannot be instantiated directly.
582 """
584 def __init__(self, *args, **kwargs) -> None:
585 raise RuntimeError( 1Y
586 "DeviceResources cannot be instantiated directly. "
587 "Use dev.resources or ctx.resources."
588 )
590 @staticmethod
591 cdef DeviceResources _init(int device_id):
592 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1zbcdAwefghijBCDEFGHIJKLMNOxPQR
593 self._device_id = device_id 1zbcdAwefghijBCDEFGHIJKLMNOxPQR
594 # _h_context is default empty — queries use cuDeviceGetDevResource
595 return self 1zbcdAwefghijBCDEFGHIJKLMNOxPQR
597 @staticmethod
598 cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id):
599 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1ayv
600 self._device_id = device_id 1ayv
601 self._h_context = h_context 1ayv
602 return self 1ayv
604 cdef inline int _query_sm(self, cydriver.CUdevResource* res) except?-1 nogil:
605 """Query SM resource from either device or context."""
606 cdef GreenCtxHandle h_green
607 if self._h_context: 1azybvcdAwefghijBCDEFGHIJKLMNO
608 h_green = get_context_green_ctx(self._h_context) 1ayv
609 if h_green: 1ayv
610 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1ayv
611 as_cu(h_green), res,
612 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
613 ))
614 else:
615 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
616 as_cu(self._h_context), res,
617 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
618 ))
619 else:
620 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1zbcdAwefghijBCDEFGHIJKLMNO
621 <cydriver.CUdevice>(self._device_id), res,
622 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1zbcdAwefghijBCDEFGHIJKLMNO
623 ))
624 return 0 1azybvcdAwefghijBCDEFGHIJKLMNO
626 @property
627 def sm(self) -> SMResource:
628 """Return the :obj:`SMResource` for this device or context."""
629 _check_green_ctx_support() 1azybvcdAwefghijBCDEFGHIJKLMNO
630 cdef cydriver.CUdevResource res
631 with nogil: 1azybvcdAwefghijBCDEFGHIJKLMNO
632 self._query_sm(&res) 1azybvcdAwefghijBCDEFGHIJKLMNO
633 return SMResource._from_dev_resource(res, self._device_id) 1azybvcdAwefghijBCDEFGHIJKLMNO
635 @property
636 def workqueue(self) -> WorkqueueResource:
637 """Return the :obj:`WorkqueueResource` for this device or context."""
638 _check_green_ctx_support() 1vwxPQR
639 _check_workqueue_support() 1vwxPQR
640 cdef cydriver.CUdevResource _wq_config
641 cdef cydriver.CUdevResource _wq
643 IF CUDA_CORE_BUILD_MAJOR >= 13:
644 cdef GreenCtxHandle h_green
645 if self._h_context: 1vwxPQR
646 h_green = get_context_green_ctx(self._h_context) 1v
647 if h_green: 1v
648 # Green context query
649 with nogil: 1v
650 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1v
651 as_cu(h_green),
652 &_wq_config,
653 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
654 ))
655 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1v
656 as_cu(h_green),
657 &_wq,
658 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
659 ))
660 else:
661 # Primary context query
662 with nogil:
663 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
664 as_cu(self._h_context),
665 &_wq_config,
666 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
667 ))
668 HANDLE_RETURN(cydriver.cuCtxGetDevResource(
669 as_cu(self._h_context),
670 &_wq,
671 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
672 ))
673 else:
674 # Device-level query
675 with nogil: 1wxPQR
676 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1wxPQR
677 <cydriver.CUdevice>(self._device_id),
678 &_wq_config,
679 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
680 ))
681 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1wxPQR
682 <cydriver.CUdevice>(self._device_id),
683 &_wq,
684 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
685 ))
686 return WorkqueueResource._from_dev_resources(_wq_config, _wq) 1vwxPQR
687 ELSE:
688 raise RuntimeError(
689 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
690 )