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

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 

9  

10from libc.stdint cimport intptr_t 

11from libc.stdlib cimport free, malloc 

12from libc.string cimport memset 

13  

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 

19  

20  

21__all__ = [ 

22 "DeviceResources", 

23 "SMResource", 

24 "SMResourceOptions", 

25 "WorkqueueResource", 

26 "WorkqueueResourceOptions", 

27] 

28  

29  

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

35  

36  

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

61  

62  

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

87  

88  

89@dataclass 

90cdef class SMResourceOptions: 

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

92  

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. 

96  

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

116  

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 

121  

122  

123@dataclass 

124cdef class WorkqueueResourceOptions: 

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

126  

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

133  

134 sharing_scope: str | None = None 

135  

136  

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

152  

153  

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 

158  

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

169  

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

189  

190  

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

195  

196  

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

204  

205  

206IF CUDA_CORE_BUILD_MAJOR >= 13: 

207 from cuda.core._resource_handles cimport sm_resource_split, has_sm_resource_split 

208  

209cdef int _structured_split_checked = 0 

210  

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 

224  

225  

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 

232  

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 ) 

243  

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 ) 

250  

251 min_count = _to_sm_count(first) 

252 return n_groups, min_count 

253  

254  

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 

266  

267 for i in range(n_groups): 1abcdmrefghijopkqlnst

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

279  

280  

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 

289  

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

295  

296 try: 1abcdmrefghijopkqlnst

297 _fill_group_params(params, n_groups, options) 1abcdmrefghijopkqlnst

298  

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

305  

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

316  

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

321  

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 ) 

338  

339  

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 

349  

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

351 if result == NULL: 

352 raise MemoryError() 

353  

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

365  

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) 

376  

377  

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

379 cdef int major 

380  

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 

390  

391  

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

396  

397  

398cdef class SMResource: 

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

400  

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

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

403 """ 

404  

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 ) 

410  

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

426  

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

448  

449 @property 

450 def handle(self) -> int: 

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

452 return <intptr_t>(&self._resource) 1X

453  

454 @property 

455 def sm_count(self) -> int: 

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

457 return self._sm_count 1ayvmXopkqln

458  

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

463  

464 @property 

465 def coscheduled_alignment(self) -> int: 

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

467 return self._coscheduled_alignment 1ZXp

468  

469 @property 

470 def flags(self) -> int: 

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

472 return self._flags 1X

473  

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. 

481  

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

489  

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) 

505  

506  

507cdef class WorkqueueResource: 

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

509  

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

515  

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

517 raise RuntimeError( 1Y

518 "WorkqueueResource cannot be instantiated directly. " 

519 "Use dev.resources.workqueue." 

520 ) 

521  

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

531  

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

536  

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

538 """Configure the workqueue resource in place. 

539  

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

552  

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 ) 

571  

572  

573cdef class DeviceResources: 

574 """Namespace for hardware resource queries. 

575  

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. 

580  

581 This class cannot be instantiated directly. 

582 """ 

583  

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 ) 

589  

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

596  

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

603  

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

625  

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

634  

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 

642  

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 )