Coverage for cuda/core/_device_resources.pyx: 77.78%

306 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-10 02:27 +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._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 

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: 1bGFcDHBdeqInAfghijklaJKLMNtOsPoQuRpSrTUVxWXyYCZ!E#3(45627$89v0mw1

46 return 0 1bGFcDHBdeqInAfghijklaJKLMNtOsPoQuRpSrTUVxWXyYCZ!E#3(45627$89v0mw1

47 if _green_ctx_checked == -1: 1aC

48 raise RuntimeError(_green_ctx_err_msg) 

49 cdef tuple drv = cy_driver_version() 1aC

50 cdef tuple bind = cy_binding_version() 1aC

51 if drv < (12, 4, 0): 1aC

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): 1aC

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 1aC

66 return 0 1aC

67  

68  

69cdef inline int _check_workqueue_support() except?-1: 

70 global _workqueue_checked, _workqueue_err_msg 

71 if _workqueue_checked == 1: 1BnA!E#3(45627$89

72 return 0 1BnA!E#3(45627$89

73 if _workqueue_checked == -1: 1AE

74 raise RuntimeError(_workqueue_err_msg) 

75 cdef tuple drv = cy_driver_version() 1AE

76 cdef tuple bind = cy_binding_version() 1AE

77 if drv < (13, 1, 0): 1AE

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): 1AE

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 1AE

92 return 0 1AE

93  

94  

95@dataclass 

96cdef class SMResourceOptions: 

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

98  

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. 

102  

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

122  

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 

127  

128  

129@dataclass 

130cdef class WorkqueueResourceOptions: 

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

132  

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

146  

147 sharing_scope: WorkqueueSharingScopeType | str | None = None 

148 concurrency_limit: int | None = None 

149  

150 def __post_init__(self): 

151 from cuda.core.typing import WorkqueueSharingScopeType 1n-.!#(52$;

152 check_str_enum(self.sharing_scope, WorkqueueSharingScopeType, allow_none=True) 1n-.!#(52$;

153 if self.concurrency_limit is not None and self.concurrency_limit < 1: 1n-.!#(52$

154 raise ValueError( 1-.

155 f"concurrency_limit must be >= 1, got {self.concurrency_limit}" 1-.

156 ) 

157  

158  

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: 1bcdeqnfghijklatsouprxy'%vmw

163 if is_sequence(value): 1cdenfghijklatsouxy'vmw

164 raise ValueError( 1'

165 f"{field_name} is a Sequence but count is scalar; " 1'

166 "count must be a Sequence to specify multiple groups" 

167 ) 

168 elif is_sequence(value) and len(value) != n_groups: 1bqpr%

169 raise ValueError( 1%

170 f"{field_name} has length {len(value)}, expected {n_groups} " 1%

171 "(must match count)" 

172 ) 

173 return 0 1bcdeqnfghijklatsouprxyvmw

174  

175  

176cdef inline int _resolve_group_count(SMResourceOptions options) except?-1: 

177 cdef object count = options.count 1bcdeqnfghijklatsoupr+,x)y'%vmw

178 cdef int n_groups 

179 cdef bint count_is_scalar 

180  

181 if count is None or isinstance(count, int): 1bcdeqnfghijklatsoupr+,x)y'%vmw

182 n_groups = 1 1cdenfghijklatsouxy'vmw

183 count_is_scalar = True 1cdenfghijklatsouxy'vmw

184 elif is_sequence(count): 1bqpr+,)%

185 n_groups = len(count) 1bqpr)%

186 if n_groups == 0: 1bqpr)%

187 raise ValueError("count sequence must not be empty") 1)

188 count_is_scalar = False 1bqpr%

189 else: 

190 raise TypeError(f"count must be int, Sequence, or None, got {type(count)}") 1+,

191  

192 _validate_split_field_length( 1bcdeqnfghijklatsouprxy'%vmw

193 options.coscheduled_sm_count, 1bcdeqnfghijklatsouprxy'%vmw

194 "coscheduled_sm_count", 

195 n_groups, 

196 count_is_scalar, 

197 ) 

198 _validate_split_field_length( 1bcdeqnfghijklatsouprxyvmw

199 options.preferred_coscheduled_sm_count, 1bcdeqnfghijklatsouprxyvmw

200 "preferred_coscheduled_sm_count", 

201 n_groups, 

202 count_is_scalar, 

203 ) 

204 _validate_split_field_length( 1bcdeqnfghijklatsouprxyvmw

205 options.backfill, 1bcdeqnfghijklatsouprxyvmw

206 "backfill", 

207 n_groups, 

208 count_is_scalar, 

209 ) 

210 return n_groups 1bcdeqnfghijklatsouprxyvmw

211  

212  

213cdef inline object _broadcast_field(object value, int n_groups): 

214 if is_sequence(value): 1bcdeqnfghijklatsouprxyvmw

215 return list(value) 1bqpr

216 return [value] * n_groups 1bcdeqnfghijklatsouprxyvmw

217  

218  

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: 1bcdeqnfghijklatsouprxyvmw

222 return 0 1cdenfghijklatsoxvmw

223 if value < 0: 1bqupry

224 raise ValueError(f"count must be non-negative, got {value}") 1y

225 return <unsigned int>(value) 1bqupr

226  

227  

228IF CUDA_CORE_BUILD_MAJOR >= 13: 

229 from cuda.core._resource_handles cimport sm_resource_split, has_sm_resource_split 

230  

231cdef int _structured_split_checked = 0 

232  

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: 1bcdeqnfghijklatsouprxyvmw

237 return _structured_split_checked == 1 1bcdeqnfghijklatsouprxyvmw

238 IF CUDA_CORE_BUILD_MAJOR >= 13: 

239 if (has_sm_resource_split() 1ay

240 and cy_driver_version() >= (13, 1, 0) 1ay

241 and cy_binding_version() >= (13, 1, 0)): 1ay

242 _structured_split_checked = 1 1ay

243 return True 1ay

244 _structured_split_checked = -1 

245 return False 

246  

247  

248cdef object _resolve_split_by_count_request(SMResourceOptions options): 

249 cdef int n_groups = _resolve_group_count(options) 

250 cdef list counts = _broadcast_field(options.count, n_groups) 

251 cdef object first = counts[0] 

252 cdef object value 

253  

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 ) 

264  

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 ) 

271  

272 cdef unsigned int min_count = _to_sm_count(first) 

273 return n_groups, min_count 

274  

275  

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

283 cdef list coscheduled = _broadcast_field(options.coscheduled_sm_count, n_groups) 1bcdeqnfghijklatsouprxyvmw

284 cdef list preferred = _broadcast_field(options.preferred_coscheduled_sm_count, n_groups) 1bcdeqnfghijklatsouprxyvmw

285 cdef list backfills = _broadcast_field(options.backfill, n_groups) 1bcdeqnfghijklatsouprxyvmw

286 cdef int i 

287  

288 for i in range(n_groups): 1bcdeqnfghijklatsouprxyvmw

289 memset(&params[i], 0, sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS)) 1bcdeqnfghijklatsouprxyvmw

290 params[i].smCount = _to_sm_count(counts[i]) 1bcdeqnfghijklatsouprxyvmw

291 if coscheduled[i] is not None: 1bcdeqnfghijklatsouprxvmw

292 params[i].coscheduledSmCount = <unsigned int>(coscheduled[i]) 1s

293 if preferred[i] is not None: 1bcdeqnfghijklatsouprxvmw

294 params[i].preferredCoscheduledSmCount = <unsigned int>(preferred[i]) 

295 params[i].flags = ( 1bcdeqnfghijklatsouprxvmw

296 cydriver.CUdevSmResourceGroup_flags.CU_DEV_SM_RESOURCE_GROUP_BACKFILL 

297 if backfills[i] else 0 1bcdeqnfghijklatsouprxvmw

298 ) 

299 return 0 1bcdeqnfghijklatsouprxvmw

300  

301  

302 cdef object _split_with_general_api(SMResource sm, SMResourceOptions options, bint dry_run): 

303 cdef int n_groups = _resolve_group_count(options) 1bcdeqnfghijklatsouprxyvmw

304 cdef cydriver.CUdevResource* result = NULL 1bcdeqnfghijklatsouprxyvmw

305 cdef cydriver.CUdevResource remaining 

306 cdef cydriver.CUdevResource synth 

307 cdef cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS* params = NULL 1bcdeqnfghijklatsouprxyvmw

308 cdef list groups = [] 1bcdeqnfghijklatsouprxyvmw

309 cdef int i 

310  

311 params = <cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS*>malloc( 1bcdeqnfghijklatsouprxyvmw

312 n_groups * sizeof(cydriver.CU_DEV_SM_RESOURCE_GROUP_PARAMS) 

313 ) 

314 if params == NULL: 1bcdeqnfghijklatsouprxyvmw

315 raise MemoryError() 

316  

317 try: 1bcdeqnfghijklatsouprxyvmw

318 _fill_group_params(params, n_groups, options) 1bcdeqnfghijklatsouprxyvmw

319  

320 if not dry_run: 1bcdeqnfghijklatsouprxvmw

321 result = <cydriver.CUdevResource*>malloc( 1bcdeqnfghijklatsouprvmw

322 n_groups * sizeof(cydriver.CUdevResource) 

323 ) 

324 if result == NULL: 1bcdeqnfghijklatsouprvmw

325 raise MemoryError() 

326  

327 memset(&remaining, 0, sizeof(cydriver.CUdevResource)) 1bcdeqnfghijklatsouprxvmw

328 with nogil: 1bcdeqnfghijklatsouprxvmw

329 HANDLE_RETURN(sm_resource_split( 1bcdeqnfghijklatsouprxvmw

330 result, 

331 <unsigned int>(n_groups), 

332 &sm._resource, 

333 &remaining, 

334 0, 

335 <void*>params, 

336 )) 

337  

338 if result != NULL: 1bcdeqnfghijklatsouprxvmw

339 for i in range(n_groups): 1bcdeqnfghijklatsouprvmw

340 groups.append(SMResource._from_split_resource(result[i], sm, True)) 1bcdeqnfghijklatsouprvmw

341 return groups, SMResource._from_split_resource(remaining, sm, True) 1bcdeqnfghijklatsouprvmw

342  

343 for i in range(n_groups): 1ox

344 memset(&synth, 0, sizeof(cydriver.CUdevResource)) 1ox

345 synth.type = cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM 1ox

346 synth.sm.smCount = params[i].smCount 1ox

347 groups.append(SMResource._from_split_resource(synth, sm, False)) 1ox

348 return groups, SMResource._from_split_resource(remaining, sm, False) 1ox

349 finally: 

350 if params != NULL: 1bcdeqnfghijklatsouprxvmw

351 free(params) 1bcdeqnfghijklatsouprxyvmw

352 if result != NULL: 1bcdeqnfghijklatsouprxyvmw

353 free(result) 1bcdeqnfghijklatsouprvmw

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 ) 

359  

360  

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 

370  

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

372 if result == NULL: 

373 raise MemoryError() 

374  

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

386  

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) 

397  

398  

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

400 cdef int major 

401  

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 

411  

412  

413cdef inline unsigned int _fallback_if_zero(unsigned int value, unsigned int fallback) noexcept: 

414 if value != 0: 1bcdeqnfghijklatsouprxvmw

415 return value 1bcdeqnfghijklatsouprvmw

416 return fallback 1cdenfghijklatsopxvmw

417  

418  

419cdef class SMResource: 

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

421  

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

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

424 """ 

425  

426 def __init__(self, *args, **kwargs): 

427 raise RuntimeError( 1:

428 "SMResource cannot be instantiated directly. " 

429 "Use dev.resources.sm or SMResource.split()." 

430 ) 

431  

432 @staticmethod 

433 cdef SMResource _from_dev_resource(cydriver.CUdevResource res, int device_id): 

434 cdef SMResource self = SMResource.__new__(SMResource) 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

435 self._resource = res 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

436 self._sm_count = res.sm.smCount 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

437 IF CUDA_CORE_BUILD_MAJOR >= 13: 

438 self._min_partition_size = res.sm.minSmPartitionSize 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

439 self._coscheduled_alignment = res.sm.smCoscheduledAlignment 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

440 self._flags = res.sm.flags 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

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 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

446 return self 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

447  

448 @staticmethod 

449 cdef SMResource _from_split_resource(cydriver.CUdevResource res, SMResource parent, bint is_usable): 

450 cdef SMResource self = SMResource.__new__(SMResource) 1bcdeqnfghijklatsouprxvmw

451 self._resource = res 1bcdeqnfghijklatsouprxvmw

452 self._sm_count = res.sm.smCount 1bcdeqnfghijklatsouprxvmw

453 IF CUDA_CORE_BUILD_MAJOR >= 13: 

454 self._min_partition_size = _fallback_if_zero( 1bcdeqnfghijklatsouprxvmw

455 res.sm.minSmPartitionSize, 

456 parent._min_partition_size, 

457 ) 

458 self._coscheduled_alignment = _fallback_if_zero( 1bcdeqnfghijklatsouprxvmw

459 res.sm.smCoscheduledAlignment, 

460 parent._coscheduled_alignment, 

461 ) 

462 self._flags = res.sm.flags 1bcdeqnfghijklatsouprxvmw

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 1bcdeqnfghijklatsouprxvmw

468 return self 1bcdeqnfghijklatsouprxvmw

469  

470 @property 

471 def handle(self) -> int: 

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

473 return <intptr_t>(&self._resource) 1/

474  

475 @property 

476 def sm_count(self) -> int: 

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

478 return self._sm_count 1bFDBq/tsoupr

479  

480 @property 

481 def min_partition_size(self) -> int: 

482 """Minimum SM count required to create a partition.""" 

483 return self._min_partition_size 1bq=/tupr'%

484  

485 @property 

486 def coscheduled_alignment(self) -> int: 

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

488 return self._coscheduled_alignment 1=/s

489  

490 @property 

491 def flags(self) -> int: 

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

493 return self._flags 1/

494  

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. 

502  

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

510  

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( 1bcdeqnfghijklatsoupr+,x)y'%vmw

518 SMResourceOptions, options, "SM resource options" 

519 ) 

520 _resolve_group_count(opts) 1bcdeqnfghijklatsoupr+,x)y'%vmw

521 _check_green_ctx_support() 1bcdeqnfghijklatsouprxyvmw

522 if _can_use_structured_sm_split(): 1bcdeqnfghijklatsouprxyvmw

523 return _split_with_general_api(self, opts, dry_run) 1bcdeqnfghijklatsouprxyvmw

524 # SplitByCount requires the same 12.4+ as green ctx support (already checked above) 

525 return _split_with_count_api(self, opts, dry_run) 

526  

527  

528cdef class WorkqueueResource: 

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

530  

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

536  

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

538 raise RuntimeError( 1:

539 "WorkqueueResource cannot be instantiated directly. " 

540 "Use dev.resources.workqueue." 

541 ) 

542  

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

549 self._wq_config_resource = wq_config 1BAE346789

550 self._wq_resource = wq 1BAE346789

551 return self 1BAE346789

552  

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) 1B*

557  

558 @property 

559 def sharing_scope(self) -> WorkqueueSharingScopeType: 

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

561  

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 152*

570 cdef object scope = self._wq_config_resource.wqConfig.sharingScope 152*

571 if scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX: 152*

572 return WorkqueueSharingScopeType.DEVICE_CTX 15*

573 elif scope == cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED: 12

574 return WorkqueueSharingScopeType.GREEN_CTX_BALANCED 12

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 ) 

580  

581 @property 

582 def concurrency_limit(self) -> int: 

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

584  

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 1#*

593 ELSE: 

594 raise RuntimeError( 

595 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings" 

596 ) 

597  

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 1*

603 return Device(int(self._wq_config_resource.wqConfig.device)) 1*

604 ELSE: 

605 raise RuntimeError( 

606 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings" 

607 ) 

608  

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

610 """Configure the workqueue resource in place. 

611  

612 Parameters 

613 ---------- 

614 options : :obj:`WorkqueueResourceOptions` 

615 Configuration options (sharing scope, concurrency limit). 

616 """ 

617 cdef WorkqueueResourceOptions opts = check_or_create_options( 1n!#(52$

618 WorkqueueResourceOptions, options, "Workqueue resource options" 

619 ) 

620 _check_green_ctx_support() 1n!#(52$

621 _check_workqueue_support() 1n!#(52$

622 if opts.sharing_scope is None and opts.concurrency_limit is None: 1n!#(52$

623 return None 1(

624  

625 IF CUDA_CORE_BUILD_MAJOR >= 13: 

626 if opts.concurrency_limit is not None: 1n!#52$

627 self._wq_config_resource.wqConfig.wqConcurrencyLimit = ( 1n!#

628 <unsigned int>opts.concurrency_limit 1n!#

629 ) 

630 if opts.sharing_scope == "device_ctx": 1n!#52$

631 self._wq_config_resource.wqConfig.sharingScope = ( 15

632 cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX 

633 ) 

634 elif opts.sharing_scope == "green_ctx_balanced": 1n!#2$

635 self._wq_config_resource.wqConfig.sharingScope = ( 1n!2$

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 ) 

642  

643  

644cdef class DeviceResources: 

645 """Namespace for hardware resource queries. 

646  

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. 

651  

652 This class cannot be instantiated directly. 

653 """ 

654  

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

656 raise RuntimeError( 1:

657 "DeviceResources cannot be instantiated directly. " 

658 "Use dev.resources or ctx.resources." 

659 ) 

660  

661 @staticmethod 

662 cdef DeviceResources _init(int device_id): 

663 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1GcHdeIAfghijklaJKLMNOPQRSTUVWXYCZE3467890m1

664 self._device_id = device_id 1GcHdeIAfghijklaJKLMNOPQRSTUVWXYCZE3467890m1

665 # _h_context is default empty — queries use cuDeviceGetDevResource 

666 return self 1GcHdeIAfghijklaJKLMNOPQRSTUVWXYCZE3467890m1

667  

668 @staticmethod 

669 cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id): 

670 cdef DeviceResources self = DeviceResources.__new__(DeviceResources) 1bFDB

671 self._device_id = device_id 1bFDB

672 self._h_context = h_context 1bFDB

673 return self 1bFDB

674  

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: 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

679 h_green = get_context_green_ctx(self._h_context) 1bFDB

680 if h_green: 1bFDB

681 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1bFB

682 as_cu(h_green), res, 

683 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 

684 )) 

685 else: 

686 HANDLE_RETURN(cydriver.cuCtxGetDevResource( 1D

687 as_cu(self._h_context), res, 

688 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1D

689 )) 

690 else: 

691 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1GcHdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

692 <cydriver.CUdevice>(self._device_id), res, 

693 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM, 1GcHdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

694 )) 

695 return 0 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

696  

697 @property 

698 def sm(self) -> SMResource: 

699 """Return the :obj:`SMResource` for this device or context.""" 

700 _check_green_ctx_support() 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

701 cdef cydriver.CUdevResource res 

702 with nogil: 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

703 self._query_sm(&res) 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

704 return SMResource._from_dev_resource(res, self._device_id) 1bGFcDHBdeIAfghijklaJKLMNOPQRSTUVWXYCZ0m1

705  

706 @property 

707 def workqueue(self) -> WorkqueueResource: 

708 """Return the :obj:`WorkqueueResource` for this device or context.""" 

709 _check_green_ctx_support() 1BAE346789

710 _check_workqueue_support() 1BAE346789

711 cdef cydriver.CUdevResource _wq_config 

712 cdef cydriver.CUdevResource _wq 

713  

714 IF CUDA_CORE_BUILD_MAJOR >= 13: 

715 cdef GreenCtxHandle h_green 

716 if self._h_context: 1BAE346789

717 h_green = get_context_green_ctx(self._h_context) 1B

718 if h_green: 1B

719 # Green context query 

720 with nogil: 1B

721 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1B

722 as_cu(h_green), 

723 &_wq_config, 

724 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG, 

725 )) 

726 HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource( 1B

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: 1AE346789

747 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1AE346789

748 <cydriver.CUdevice>(self._device_id), 

749 &_wq_config, 

750 cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG, 

751 )) 

752 HANDLE_RETURN(cydriver.cuDeviceGetDevResource( 1AE346789

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

758 ELSE: 

759 raise RuntimeError( 

760 "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings" 

761 )