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

1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 

2# 

3# SPDX-License-Identifier: Apache-2.0 

4  

5from __future__ import annotations 

6  

7from collections.abc import Sequence as SequenceABC 

8from dataclasses import dataclass 

9from typing import TYPE_CHECKING 

10  

11if TYPE_CHECKING: 

12 from cuda.core._device import Device 

13 from cuda.core.typing import WorkqueueSharingScopeType 

14  

15from libc.stdint cimport intptr_t 

16from libc.stdlib cimport free, malloc 

17from libc.string cimport memset 

18  

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 

25  

26  

27__all__ = [ 

28 "DeviceResources", 

29 "SMResource", 

30 "SMResourceOptions", 

31 "WorkqueueResource", 

32 "WorkqueueResourceOptions", 

33] 

34  

35  

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 = "" 

41  

42  

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

59  

60  

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

77  

78  

79@dataclass 

80cdef class SMResourceOptions: 

81 """Customizable :obj:`SMResource.split` options. 

82  

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. 

86  

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 """ 

106  

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 

111  

112  

113@dataclass 

114cdef class WorkqueueResourceOptions: 

115 """Customizable :obj:`WorkqueueResource.configure` options. 

116  

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 """ 

130  

131 sharing_scope: WorkqueueSharingScopeType | str | None = None 

132 concurrency_limit: int | None = None 

133  

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 ) 

141  

142  

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

158  

159  

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 

164  

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,-

175  

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

195  

196  

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

201  

202  

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

210  

211  

212cdef int _structured_split_checked = 0 

213  

214cdef inline bint _can_use_structured_sm_split(): 

215 """Whether the driver provides cuDevSmResourceSplit, a 13.1 driver API. Cached. 

216  

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 

227  

228  

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 

234  

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 ) 

245  

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 ) 

252  

253 cdef unsigned int min_count = _to_sm_count(first) 

254 return n_groups, min_count 

255  

256  

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 

268  

269 for i in range(n_groups): 1bdefqnghijklmatsouprxzvcw

270 memset(&params[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

281  

282  

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 

291  

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() 

297  

298 try: 1bdefqnghijklmatsouprxzvcw

299 _fill_group_params(params, n_groups, options) 1bdefqnghijklmatsouprxzvcw

300  

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() 

307  

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 )) 

318  

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

323  

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 ) 

340  

341  

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 

351  

352 result = <cydriver.CUdevResource*>malloc(nb_groups * sizeof(cydriver.CUdevResource)) 

353 if result == NULL: 

354 raise MemoryError() 

355  

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 )) 

367  

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) 

378  

379  

380cdef inline unsigned int _sm_resource_granularity(int device_id) except? 0: 

381 cdef int major 

382  

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 

392  

393  

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

398  

399  

400cdef class SMResource: 

401 """Represent an SM (streaming multiprocessor) resource partition. 

402  

403 Instances are returned by :obj:`DeviceResources.sm` or 

404 :meth:`SMResource.split` and cannot be instantiated directly. 

405 """ 

406  

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 ) 

412  

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

428  

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

450  

451 @property 

452 def handle(self) -> int: 

453 """Return the address of the underlying ``CUdevResource`` struct.""" 

454 return <intptr_t>(&self._resource) 1:

455  

456 @property 

457 def sm_count(self) -> int: 

458 """Total SMs available in this resource.""" 

459 return self._sm_count 1bEDyq:tsoupr

460  

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('

465  

466 @property 

467 def coscheduled_alignment(self) -> int: 

468 """Number of SMs guaranteed to be co-scheduled.""" 

469 return self._coscheduled_alignment 1?:s

470  

471 @property 

472 def flags(self) -> int: 

473 """Raw flags from the underlying SM resource.""" 

474 return self._flags 1:

475  

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. 

483  

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``) 

491  

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) 

507  

508  

509cdef class WorkqueueResource: 

510 """Represent a workqueue resource for a device or green context. 

511  

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 """ 

517  

518 def __init__(self, *args, **kwargs) -> None: 

519 raise RuntimeError( 1;

520 "WorkqueueResource cannot be instantiated directly. " 

521 "Use dev.resources.workqueue." 

522 ) 

523  

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!

533  

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+

538  

539 @property 

540 def sharing_scope(self) -> WorkqueueSharingScopeType: 

541 """Current sharing scope of this workqueue resource. 

542  

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 ) 

561  

562 @property 

563 def concurrency_limit(self) -> int: 

564 """Current expected maximum concurrent stream-ordered workloads. 

565  

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 ) 

578  

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 ) 

589  

590 def configure(self, WorkqueueResourceOptions options not None: WorkqueueResourceOptions) -> None: 

591 """Configure the workqueue resource in place. 

592  

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)

605  

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 ) 

623  

624  

625cdef class DeviceResources: 

626 """Namespace for hardware resource queries. 

627  

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. 

632  

633 This class cannot be instantiated directly. 

634 """ 

635  

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 ) 

641  

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

648  

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

655  

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

677  

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

686  

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 

694  

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 )