Coverage for cuda/core/system/_device.pyx: 56.72%

268 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-16 01:23 +0000

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

2# 

3# SPDX-License-Identifier: Apache-2.0 

4  

5from __future__ import annotations 

6  

7from libc.stdint cimport intptr_t, uint64_t 

8from libc.math cimport ceil 

9  

10from multiprocessing import cpu_count 

11from typing import Iterable, TYPE_CHECKING 

12import warnings 

13  

14from cuda.bindings import nvml 

15  

16from ._nvml_context cimport initialize 

17from cuda.core.system.typing import ( 

18 AddressingMode, 

19 AffinityScope, 

20 DeviceArch, 

21 ClockId, 

22 ClocksEventReasons, 

23 ClockType, 

24 CoolerControl, 

25 CoolerTarget, 

26 DeviceArch, 

27 EventType, 

28 FanControlPolicy, 

29 FieldId, 

30 GpuP2PCapsIndex, 

31 GpuP2PStatus, 

32 GpuTopologyLevel, 

33 InforomObject, 

34 TemperatureThresholds, 

35 ThermalController, 

36 ThermalTarget, 

37) 

38  

39if TYPE_CHECKING: 

40 import cuda.core # no-cython-lint 

41  

42  

43cdef object _pstate_to_int(object pstate): 

44 if pstate == nvml.Pstates.PSTATE_UNKNOWN: 1fc

45 return None 1c

46 assert ( 1fc

47 int(pstate) >= 0 and int(pstate) <= 15 1fc

48 ), f"Invalid P-state: {pstate}. Must be between 0 and 15 inclusive, or PSTATE_UNKNOWN." 

49 return int(pstate) - int(nvml.Pstates.PSTATE_0) 1fc

50  

51  

52cdef int _pstate_to_enum(int pstate): 

53 if pstate < 0 or pstate > 15: 1f

54 raise ValueError(f"Invalid P-state: {pstate}. Must be between 0 and 15 inclusive.") 

55 return int(pstate) + int(nvml.Pstates.PSTATE_0) 1f

56  

57  

58include "_clock.pxi" 

59include "_cooler.pxi" 

60include "_device_attributes.pxi" 

61include "_device_utils.pxi" 

62include "_event.pxi" 

63include "_fan.pxi" 

64include "_field_values.pxi" 

65include "_inforom.pxi" 

66include "_memory.pxi" 

67include "_mig.pxi" 

68include "_nvlink.pxi" 

69include "_pci_info.pxi" 

70include "_performance.pxi" 

71include "_process.pxi" 

72include "_repair_status.pxi" 

73include "_temperature.pxi" 

74include "_utilization.pxi" 

75  

76  

77_ADDRESSING_MODE_MAPPING = { 

78 nvml.DeviceAddressingModeType.DEVICE_ADDRESSING_MODE_HMM: AddressingMode.HMM, 

79 nvml.DeviceAddressingModeType.DEVICE_ADDRESSING_MODE_ATS: AddressingMode.ATS, 

80} 

81  

82  

83_AFFINITY_SCOPE_MAPPING = { 

84 AffinityScope.NODE: nvml.AffinityScope.NODE, 

85 AffinityScope.SOCKET: nvml.AffinityScope.SOCKET, 

86} 

87  

88  

89_BRAND_TYPE_MAPPING = { 

90 nvml.BrandType.BRAND_UNKNOWN: "Unknown", 

91 nvml.BrandType.BRAND_QUADRO: "Quadro", 

92 nvml.BrandType.BRAND_TESLA: "Tesla", 

93 nvml.BrandType.BRAND_NVS: "NVS", 

94 nvml.BrandType.BRAND_GRID: "GRID", 

95 nvml.BrandType.BRAND_GEFORCE: "GeForce", 

96 nvml.BrandType.BRAND_TITAN: "Titan", 

97 nvml.BrandType.BRAND_NVIDIA_VAPPS: "NVIDIA vApps", 

98 nvml.BrandType.BRAND_NVIDIA_VPC: "NVIDIA VPC", 

99 nvml.BrandType.BRAND_NVIDIA_VCS: "NVIDIA VCS", 

100 nvml.BrandType.BRAND_NVIDIA_VWS: "NVIDIA VWS", 

101 nvml.BrandType.BRAND_NVIDIA_CLOUD_GAMING: "NVIDIA Cloud Gaming", 

102 nvml.BrandType.BRAND_NVIDIA_VGAMING: "NVIDIA vGaming", 

103 nvml.BrandType.BRAND_QUADRO_RTX: "Quadro RTX", 

104 nvml.BrandType.BRAND_NVIDIA_RTX: "NVIDIA RTX", 

105 nvml.BrandType.BRAND_NVIDIA: "NVIDIA", 

106 nvml.BrandType.BRAND_GEFORCE_RTX: "GeForce RTX", 

107 nvml.BrandType.BRAND_TITAN_RTX: "Titan RTX", 

108 nvml.BrandType.BRAND_NVIDIA_DLA: "NVIDIA DLA", 

109 nvml.BrandType.BRAND_NVIDIA_VGAMEDEV: "NVIDIA vGameDev", 

110 nvml.BrandType.BRAND_NVIDIA_NPU: "NVIDIA NPU", 

111} 

112  

113  

114_GPU_P2P_CAPS_INDEX_MAPPING = { 

115 GpuP2PCapsIndex.READ: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_READ, 

116 GpuP2PCapsIndex.WRITE: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_WRITE, 

117 GpuP2PCapsIndex.NVLINK: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_NVLINK, 

118 GpuP2PCapsIndex.ATOMICS: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_ATOMICS, 

119 GpuP2PCapsIndex.PCI: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_PCI, 

120 GpuP2PCapsIndex.PROP: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_PROP, 

121 GpuP2PCapsIndex.UNKNOWN: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_UNKNOWN, 

122} 

123  

124  

125_GPU_P2P_STATUS_MAPPING = { 

126 nvml.GpuP2PStatus.P2P_STATUS_OK: GpuP2PStatus.OK, 

127 nvml.GpuP2PStatus.P2P_STATUS_CHIPSET_NOT_SUPPORTED: GpuP2PStatus.CHIPSET_NOT_SUPPORTED, 

128 nvml.GpuP2PStatus.P2P_STATUS_GPU_NOT_SUPPORTED: GpuP2PStatus.GPU_NOT_SUPPORTED, 

129 nvml.GpuP2PStatus.P2P_STATUS_IOH_TOPOLOGY_NOT_SUPPORTED: GpuP2PStatus.IOH_TOPOLOGY_NOT_SUPPORTED, 

130 nvml.GpuP2PStatus.P2P_STATUS_DISABLED_BY_REGKEY: GpuP2PStatus.DISABLED_BY_REGKEY, 

131 nvml.GpuP2PStatus.P2P_STATUS_NOT_SUPPORTED: GpuP2PStatus.NOT_SUPPORTED, 

132 nvml.GpuP2PStatus.P2P_STATUS_UNKNOWN: GpuP2PStatus.UNKNOWN, 

133} 

134  

135  

136_GPU_TOPOLOGY_LEVEL_MAPPING = { 

137 GpuTopologyLevel.INTERNAL: nvml.GpuTopologyLevel.TOPOLOGY_INTERNAL, 

138 GpuTopologyLevel.SINGLE: nvml.GpuTopologyLevel.TOPOLOGY_SINGLE, 

139 GpuTopologyLevel.MULTIPLE: nvml.GpuTopologyLevel.TOPOLOGY_MULTIPLE, 

140 GpuTopologyLevel.HOSTBRIDGE: nvml.GpuTopologyLevel.TOPOLOGY_HOSTBRIDGE, 

141 GpuTopologyLevel.NODE: nvml.GpuTopologyLevel.TOPOLOGY_NODE, 

142 GpuTopologyLevel.SYSTEM: nvml.GpuTopologyLevel.TOPOLOGY_SYSTEM, 

143} 

144  

145  

146_GPU_TOPOLOGY_LEVEL_INV_MAPPING = {v: k for k, v in _GPU_TOPOLOGY_LEVEL_MAPPING.items()} 

147  

148  

149cdef class Device: 

150 """ 

151 Representation of a device. 

152  

153 :class:`cuda.core.system.Device` provides access to various pieces of metadata 

154 about devices and their topology, as provided by the NVIDIA Management 

155 Library (NVML). To use CUDA with a device, use :class:`cuda.core.Device`. 

156  

157 Creating a device instance causes NVML to initialize the target GPU. 

158 NVML may initialize additional GPUs if the target GPU is an SLI slave. 

159  

160 Parameters 

161 ---------- 

162 index: int, optional 

163 Integer representing the CUDA device index to get a handle to. Valid 

164 values are between ``0`` and ``cuda.core.system.get_num_devices() - 1``. 

165  

166 The order in which devices are enumerated has no guarantees of 

167 consistency between reboots. For that reason, it is recommended that 

168 devices are looked up by their PCI ids or UUID. 

169  

170 uuid: bytes or str, optional 

171 UUID of a CUDA device to get a handle to. 

172  

173 pci_bus_id: bytes or str, optional 

174 PCI bus ID of a CUDA device to get a handle to. 

175  

176 Raises 

177 ------ 

178 ValueError 

179 If anything other than a single `index`, `uuid` or `pci_bus_id` are specified. 

180 """ 

181  

182 # This is made public for testing purposes only 

183 cdef public intptr_t _handle 

184  

185 def __init__( 

186 self, 

187 *, 

188 index: int | None = None, 

189 uuid: bytes | str | None = None, 

190 pci_bus_id: bytes | str | None = None, 

191 ) -> None: 

192 args = [index, uuid, pci_bus_id] 1sbtudfvJpawxKVhQyLkqzoAMlBNrOCDEgnciFTUGeHPjRSm

193 cdef int arg_count = sum(arg is not None for arg in args) 1sbtudfvJpawxKVhQyLkqzoAMlBNrOCDEgnciFTUGeHPjRSm

194  

195 if arg_count > 1: 1sbtudfvJpawxKVhQyLkqzoAMlBNrOCDEgnciFTUGeHPjRSm

196 raise ValueError("Handle requires only one of `index`, `uuid`, or `pci_bus_id`.") 1V

197 if arg_count == 0: 1sbtudfvJpawxKVhQyLkqzoAMlBNrOCDEgnciFTUGeHPjRSm

198 raise ValueError("Handle requires either a device `index`, `uuid`, or `pci_bus_id`.") 1V

199  

200 initialize() 1sbtudfvJpawxKhQyLkqzoAMlBNrOCDEgnciFTUGeHPjRSm

201  

202 if index is not None: 1sbtudfvJpawxKhQyLkqzoAMlBNrOCDEgnciFTUGeHPjRSm

203 self._handle = nvml.device_get_handle_by_index_v2(index) 1sbtudfpawxKhyLzoAlBNrOCDEgnciFTUGeHP

204 elif uuid is not None: 1vJQkqMjRSm

205 if isinstance(uuid, bytes): 1vJQkqMjRSm

206 uuid = uuid.decode("ascii") 

207 self._handle = nvml.device_get_handle_by_uuid(uuid) 1vJQkqMjRSm

208 elif pci_bus_id is not None: 1kj

209 if isinstance(pci_bus_id, bytes): 1kj

210 pci_bus_id = pci_bus_id.decode("ascii") 

211 self._handle = nvml.device_get_handle_by_pci_bus_id_v2(pci_bus_id) 1kj

212  

213 ######################################################################### 

214 # BASIC PROPERTIES 

215  

216 @property 

217 def index(self) -> int: 

218 """ 

219 The NVML index of this device. 

220  

221 Valid indices are derived from the count returned by 

222 :meth:`Device.get_device_count`. For example, if ``get_device_count()`` 

223 returns 2, the valid indices are 0 and 1, corresponding to GPU 0 and GPU 

224 1. 

225  

226 The order in which NVML enumerates devices has no guarantees of 

227 consistency between reboots. For that reason, it is recommended that 

228 devices be looked up by their PCI ids or GPU UUID. 

229  

230 Note: The NVML index may not correlate with other APIs, such as the CUDA 

231 device index. 

232 """ 

233 return nvml.device_get_index(self._handle) 1sbtudfvJwxhykqzMlIBOCDEgncFGeHj

234  

235 @property 

236 def uuid(self) -> str: 

237 """ 

238 Retrieves the globally unique immutable UUID associated with this 

239 device, as a 5 part hexadecimal string, that augments the immutable, 

240 board serial identifier. 

241  

242 Returns the UUID exactly as reported by NVML. It usually includes a 

243 ``GPU-``, ``MIG-``, or ``DLA-`` prefix, but some platforms report an 

244 unprefixed UUID. To interact with CUDA, use the `uuid_without_prefix` 

245 property. 

246 """ 

247 return nvml.device_get_uuid(self._handle) 1oePWXYZm

248  

249 @property 

250 def uuid_without_prefix(self) -> str: 

251 """ 

252 Retrieves the globally unique immutable UUID associated with this 

253 device, as a 5 part hexadecimal string, that augments the immutable, 

254 board serial identifier. 

255  

256 Removes a ``GPU-``, ``MIG-``, or ``DLA-`` prefix when present, to match 

257 the UUIDs used in CUDA. An already unprefixed UUID is returned 

258 unchanged. For the UUID exactly as reported by NVML, use the `uuid` 

259 property. 

260 """ 

261 uuid = self.uuid 1oeWXYZm

262 if uuid.startswith(("GPU-", "MIG-", "DLA-")): 1oeWXYZm

263 return uuid[4:] 1oeWXYm

264 return uuid 1Z

265  

266 @property 

267 def pci_bus_id(self) -> str: 

268 """ 

269 Retrieves the PCI bus ID of this device. 

270 """ 

271 return self.pci_info.bus_id 1j

272  

273 @property 

274 def numa_node_id(self) -> int: 

275 """ 

276 The NUMA node of the given GPU device. 

277  

278 This only applies to platforms where the GPUs are NUMA nodes. 

279 """ 

280 return nvml.device_get_numa_node_id(self._handle) 1E

281  

282 @property 

283 def arch(self) -> DeviceArch: 

284 """ 

285 :obj:`~DeviceArch` device architecture. 

286  

287 For example, a Tesla V100 will report ``DeviceArchitecture.name == 

288 "VOLTA"``, and RTX A6000 will report ``DeviceArchitecture.name == 

289 "AMPERE"``. 

290 """ 

291 arch = nvml.device_get_architecture(self._handle) 1p

292 try: 1p

293 return DeviceArch(arch) 1p

294 except ValueError: 

295 return DeviceArch.UNKNOWN 

296  

297 @property 

298 def name(self) -> str: 

299 """ 

300 Name of the device, e.g.: `"Tesla V100-SXM2-32GB"` 

301 """ 

302 return nvml.device_get_name(self._handle) 1L

303  

304 @property 

305 def brand(self) -> str: 

306 """ 

307 The brand of the device. 

308  

309 Returns "Unknown" if the brand is unknown. 

310 """ 

311 return _BRAND_TYPE_MAPPING.get(nvml.device_get_brand(self._handle), "Unknown") 1K

312  

313 @property 

314 def serial(self) -> str: 

315 """ 

316 Retrieves the globally unique board serial number associated with this 

317 device's board. 

318  

319 For all products with an InfoROM. 

320 """ 

321 return nvml.device_get_serial(self._handle) 1z

322  

323 @property 

324 def module_id(self) -> int: 

325 """ 

326 Get a unique identifier for the device module on the baseboard. 

327  

328 This API retrieves a unique identifier for each GPU module that exists 

329 on a given baseboard. For non-baseboard products, this ID would always 

330 be 0. 

331 """ 

332 return nvml.device_get_module_id(self._handle) 1D

333  

334 @property 

335 def minor_number(self) -> int: 

336 """ 

337 The minor number of this device. 

338  

339 For Linux only. 

340  

341 The minor number is used by the Linux device driver to identify the 

342 device node in ``/dev/nvidiaX``. 

343 """ 

344 return nvml.device_get_minor_number(self._handle) 1N

345  

346 @property 

347 def is_c2c_enabled(self) -> bool: 

348 """ 

349 Whether the C2C (Chip-to-Chip) mode is enabled for this device. 

350 """ 

351 return bool(nvml.device_get_c2c_mode_info_v(self._handle).is_c2c_enabled) 1u

352  

353 @property 

354 def is_persistence_mode_enabled(self) -> bool: 

355 """ 

356 Whether persistence mode is enabled for this device. 

357  

358 For Linux only. 

359 """ 

360 return nvml.device_get_persistence_mode(self._handle) == nvml.EnableState.FEATURE_ENABLED 1n

361  

362 @is_persistence_mode_enabled.setter 

363 def is_persistence_mode_enabled(self, enabled: bool) -> None: 

364 nvml.device_set_persistence_mode( 1n

365 self._handle, 1n

366 nvml.EnableState.FEATURE_ENABLED if enabled else nvml.EnableState.FEATURE_DISABLED 1n

367 ) 

368  

369 @property 

370 def cuda_compute_capability(self) -> tuple[int, int]: 

371 """ 

372 CUDA compute capability of the device, e.g.: `(7, 0)` for a Tesla V100. 

373  

374 Returns a tuple `(major, minor)`. 

375 """ 

376 return nvml.device_get_cuda_compute_capability(self._handle) 1Q

377  

378 def to_cuda_device(self) -> "cuda.core.Device": 

379 """ 

380 Get the corresponding :class:`cuda.core.Device` (which is used for CUDA 

381 access) for this :class:`cuda.core.system.Device` (which is used for 

382 NVIDIA Management Library (NVML) access). 

383  

384 The devices are mapped to one another by their UUID. 

385  

386 Returns 

387 ------- 

388 cuda.core.Device 

389 The corresponding CUDA device. 

390  

391 Raises 

392 ------ 

393 RuntimeError 

394 No corresponding CUDA device is found for this NVML device. 

395  

396 For example, on a MIG system, the physical GPU will not have an 

397 available CUDA device, since it can not be used directly, even 

398 though it can be enumerated from NVML. 

399 """ 

400 from cuda.core import Device as CudaDevice 1e

401  

402 # CUDA does not have an API to get a device by its UUID, so we just 

403 # search all the devices for one with a matching UUID. 

404  

405 for cuda_device in CudaDevice.get_all_devices(): 1e

406 if cuda_device.uuid == self.uuid_without_prefix: 1e

407 return cuda_device 1e

408  

409 raise RuntimeError("No corresponding CUDA device found for this NVML device.") 

410  

411 @classmethod 

412 def get_device_count(cls) -> int: 

413 """ 

414 Get the number of available devices. 

415  

416 Returns 

417 ------- 

418 int 

419 The number of available devices. 

420 """ 

421 initialize() 1567

422  

423 return nvml.device_get_count_v2() 1567

424  

425 @classmethod 

426 def get_all_devices(cls) -> Iterable[Device]: 

427 """ 

428 Query the available device instances. 

429  

430 Returns 

431 ------- 

432 Iterator over :obj:`~Device` 

433 An iterator over available devices. 

434 """ 

435 initialize() 1sbtudfpwxKhyLzoAlBNrOCDEgnciFGeHP

436  

437 for device_id in range(nvml.device_get_count_v2()): 1sbtudfpwxKhyLzoAlBNrOCDEgnciFGeHP

438 yield cls(index=device_id) 1sbtudfpwxKhyLzoAlBNrOCDEgnciFGeHP

439  

440 ######################################################################### 

441 # ADDRESSING MODE 

442  

443 @property 

444 def addressing_mode(self) -> AddressingMode | None: 

445 """ 

446 Get the :obj:`~AddressingMode` of the device. 

447 """ 

448 return _ADDRESSING_MODE_MAPPING.get(nvml.device_get_addressing_mode(self._handle).value, None) 1s

449  

450 ######################################################################### 

451 # MIG (MULTI-INSTANCE GPU) DEVICES 

452  

453 @property 

454 def mig(self) -> MigInfo: 

455 """ 

456 Get :obj:`~MigInfo` accessor for MIG (Multi-Instance GPU) information. 

457  

458 For Ampere™ or newer fully supported devices. 

459 """ 

460 return MigInfo(self) 1vC

461  

462 ######################################################################### 

463 # AFFINITY 

464  

465 @classmethod 

466 def get_all_devices_with_cpu_affinity(cls, cpu_index: int) -> Iterable[Device]: 

467 """ 

468 Retrieve the set of GPUs that have a CPU affinity with the given CPU number. 

469  

470 Supported on Linux only. 

471  

472 Parameters 

473 ---------- 

474 cpu_index: int 

475 The CPU index. 

476  

477 Returns 

478 ------- 

479 Iterator of :obj:`~Device` 

480 An iterator over available devices. 

481 """ 

482 cdef Device device 

483 for handle in nvml.system_get_topology_gpu_set(cpu_index): 1I

484 device = Device.__new__(Device) 1I

485 device._handle = handle 1I

486 yield device 1I

487  

488 def get_memory_affinity(self, scope: AffinityScope | str=AffinityScope.NODE) -> list[int]: 

489 """ 

490 Retrieves a list of indices of NUMA nodes or CPU sockets with the ideal 

491 memory affinity for the device. 

492  

493 For Kepler™ or newer fully supported devices. 

494  

495 Supported on Linux only. 

496  

497 If requested scope is not applicable to the target topology, the API 

498 will fall back to reporting the memory affinity for the immediate non-I/O 

499 ancestor of the device. 

500  

501 Parameters 

502 ---------- 

503 scope: AffinityScope | str, optional 

504 The scope of the affinity query. Must be one of the values of 

505 :class:`AffinityScope`. Default is :attr:`AffinityScope.NODE`. 

506  

507 Returns 

508 ------- 

509 list[int] 

510 A list of indices of NUMA nodes or CPU sockets with the ideal memory 

511 affinity for the device. 

512 """ 

513 try: 1ba

514 scope = _AFFINITY_SCOPE_MAPPING[scope] 1ba

515 except KeyError: 1a

516 raise ValueError( 1a

517 f"Invalid affinity scope: {scope}. " 1a

518 f"Must be one of {list(AffinityScope.__members__.values())}" 1a

519 ) from None 1a

520 return _unpack_bitmask( 1b

521 nvml.device_get_memory_affinity( 1b

522 self._handle, 1b

523 <unsigned int>ceil(cpu_count() / 64), 1b

524 scope, 1b

525 ) 

526 ) 

527  

528 def get_cpu_affinity(self, scope: AffinityScope | str=AffinityScope.NODE) -> list[int]: 

529 """ 

530 Retrieves a list of indices of NUMA nodes or CPU sockets with the ideal 

531 CPU affinity for the device. 

532  

533 For Kepler™ or newer fully supported devices. 

534  

535 Supported on Linux only. 

536  

537 If requested scope is not applicable to the target topology, the API 

538 will fall back to reporting the memory affinity for the immediate non-I/O 

539 ancestor of the device. 

540  

541 Parameters 

542 ---------- 

543 scope: AffinityScope | str, optional 

544 The scope of the affinity query. Must be one of the values of 

545 :class:`AffinityScope`. Default is :attr:`AffinityScope.NODE`. 

546  

547 Returns 

548 ------- 

549 list[int] 

550 A list of indices of NUMA nodes or CPU sockets with the ideal memory 

551 affinity for the device. 

552 """ 

553 try: 1bahI

554 scope = _AFFINITY_SCOPE_MAPPING[scope] 1bahI

555 except KeyError: 1a

556 raise ValueError( 1a

557 f"Invalid affinity scope: {scope}. " 1a

558 f"Must be one of {list(AffinityScope.__members__.values())}" 1a

559 ) from None 1a

560 return _unpack_bitmask( 1bhI

561 nvml.device_get_cpu_affinity_within_scope( 1bhI

562 self._handle, 1bhI

563 <unsigned int>ceil(cpu_count() / 64), 1bhI

564 scope, 1bhI

565 ) 

566 ) 

567  

568 def set_cpu_affinity(self) -> None: 

569 """ 

570 Sets the ideal affinity for the calling thread and device. 

571  

572 For Kepler™ or newer fully supported devices. 

573  

574 Supported on Linux only. 

575 """ 

576 nvml.device_set_cpu_affinity(self._handle) 

577  

578 def clear_cpu_affinity(self) -> None: 

579 """ 

580 Clear all affinity bindings for the calling thread. 

581  

582 For Kepler™ or newer fully supported devices. 

583  

584 Supported on Linux only. 

585 """ 

586 nvml.device_clear_cpu_affinity(self._handle) 

587  

588 ######################################################################### 

589 # CLOCK 

590 # See external class definitions in _clock.pxi 

591  

592 def get_clock(self, clock_type: ClockType | str) -> ClockInfo: 

593 """ 

594 :obj:`~_device.ClockInfo` object to get information about and manage a specific clock on a device. 

595 """ 

596 return ClockInfo(self._handle, clock_type) 1f

597  

598 @property 

599 def is_auto_boosted_clocks_enabled(self) -> tuple[bool, bool]: 

600 """ 

601 Retrieve the current state of auto boosted clocks on a device. 

602  

603 For Kepler™ or newer fully supported devices. 

604  

605 Auto Boosted clocks are enabled by default on some hardware, allowing 

606 the GPU to run at higher clock rates to maximize performance as thermal 

607 limits allow. 

608  

609 On Pascal™ and newer hardware, Auto Boosted clocks are controlled 

610 through application clocks. Use :meth:`set_application_clocks` and 

611 :meth:`reset_application_clocks` to control Auto Boost behavior. 

612  

613 Returns 

614 ------- 

615 bool 

616 The current state of Auto Boosted clocks 

617 bool 

618 The default Auto Boosted clocks behavior 

619  

620 """ 

621 current, default = nvml.device_get_auto_boosted_clocks_enabled(self._handle) 1t

622 return current == nvml.EnableState.FEATURE_ENABLED, default == nvml.EnableState.FEATURE_ENABLED 

623  

624 @property 

625 def current_clock_event_reasons(self) -> list[ClocksEventReasons]: 

626 """ 

627 Retrieves the current :obj:`~ClocksEventReasons`. 

628  

629 For all fully supported products. 

630 """ 

631 cdef uint64_t[1] reasons 

632 reasons[0] = nvml.device_get_current_clocks_event_reasons(self._handle) 1d

633 output_reasons = [] 1d

634 for reason in _unpack_bitmask(reasons): 1d

635 try: 

636 output_reason = _CLOCKS_EVENT_REASONS_MAPPING[1 << reason] 

637 except KeyError: 

638 raise ValueError(f"Unknown clock event reason bit: {1 << reason}") 

639 output_reasons.append(output_reason) 

640 return output_reasons 1d

641  

642 @property 

643 def supported_clock_event_reasons(self) -> list[ClocksEventReasons]: 

644 """ 

645 Retrieves supported :obj:`~ClocksEventReasons` that can be returned by 

646 :meth:`get_current_clock_event_reasons`. 

647  

648 For all fully supported products. 

649  

650 This method is not supported in virtual machines running virtual GPU (vGPU). 

651 """ 

652 cdef uint64_t[1] reasons 

653 reasons[0] = nvml.device_get_supported_clocks_event_reasons(self._handle) 1d

654 output_reasons = [] 1d

655 for reason in _unpack_bitmask(reasons): 1d

656 try: 1d

657 output_reason = _CLOCKS_EVENT_REASONS_MAPPING[1 << reason] 1d

658 except KeyError: 

659 raise ValueError(f"Unknown clock event reason bit: {1 << reason}") 

660 output_reasons.append(output_reason) 1d

661 return output_reasons 1d

662  

663 ########################################################################## 

664 # COOLER 

665 # See external class definitions in _cooler.pxi 

666  

667 @property 

668 def cooler(self) -> CoolerInfo: 

669 """ 

670 :obj:`~_device.CoolerInfo` object with cooler information for the device. 

671 """ 

672 return CoolerInfo(nvml.device_get_cooler_info(self._handle)) 

673  

674 ########################################################################## 

675 # DEVICE ATTRIBUTES 

676 # See external class definitions in _device_attributes.pxi 

677  

678 @property 

679 def attributes(self) -> DeviceAttributes: 

680 """ 

681 :obj:`~_device.DeviceAttributes` object with various device attributes. 

682  

683 For Ampere™ or newer fully supported devices. Only available on Linux 

684 systems. 

685 """ 

686 return DeviceAttributes(nvml.device_get_attributes_v2(self._handle)) 1w

687  

688 ######################################################################### 

689 # DISPLAY 

690  

691 @property 

692 def is_display_connected(self) -> bool: 

693 """ 

694 The display mode for this device. 

695  

696 Indicates whether a physical display (e.g. monitor) is currently connected to 

697 any of the device's connectors. 

698 """ 

699 return nvml.device_get_display_mode(self._handle) == nvml.EnableState.FEATURE_ENABLED 1A

700  

701 @property 

702 def is_display_active(self) -> bool: 

703 """ 

704 The display active status for this device. 

705  

706 Indicates whether a display is initialized on the device. For example, 

707 whether X Server is attached to this device and has allocated memory for 

708 the screen. 

709  

710 Display can be active even when no monitor is physically attached. 

711 """ 

712 return nvml.device_get_display_active(self._handle) == nvml.EnableState.FEATURE_ENABLED 1A

713  

714 ########################################################################## 

715 # EVENTS 

716 # See external class definitions in _event.pxi 

717  

718 def register_events(self, events: EventType | str | list[EventType | str]) -> DeviceEvents: 

719 """ 

720 Starts recording events on this device. 

721  

722 For Fermi™ or newer fully supported devices. For Linux only. 

723  

724 ECC events are available only on ECC-enabled devices (see 

725 :meth:`Device.get_total_ecc_errors`). Power capping events are 

726 available only on Power Management enabled devices (see 

727 :meth:`Device.get_power_management_mode`). 

728  

729 This call starts recording of events on specific device. All events 

730 that occurred before this call are not recorded. Wait for events using 

731 the :meth:`DeviceEvents.wait` method on the result. 

732  

733 Examples 

734 -------- 

735 >>> device = Device(index=0) 

736 >>> events = device.register_events([ 

737 ... EventType.XID_CRITICAL_ERROR, 

738 ... ]) 

739 >>> while event := events.wait(timeout_ms=10000): 

740 ... print(f"Event {event.event_type} occurred on device {event.device.uuid}") 

741  

742 Parameters 

743 ---------- 

744 events: EventType, str, or list of EventType or str 

745 The event type or list of event types to register for this device. 

746  

747 Returns 

748 ------- 

749 :obj:`~_device.DeviceEvents` 

750 An object representing the registered events. Call 

751 :meth:`~_device.DeviceEvents.wait` on this object to wait for events. 

752  

753 Raises 

754 ------ 

755 :class:`cuda.core.system.NotSupportedError` 

756 None of the requested event types are registered. 

757 """ 

758 return DeviceEvents(self._handle, events) 1i

759  

760 def get_supported_event_types(self) -> list[EventType]: 

761 """ 

762 Get the list of event types supported by this device. 

763  

764 For Fermi™ or newer fully supported devices. For Linux only (returns an 

765 empty list on Windows). 

766  

767 Returns 

768 ------- 

769 list[EventType] 

770 The list of supported event types. 

771 """ 

772 cdef uint64_t[1] bitmask 

773 bitmask[0] = nvml.device_get_supported_event_types(self._handle) 1i

774 events = [] 1i

775 for ev in _unpack_bitmask(bitmask): 1i

776 try: 1i

777 ev_enum = _EVENT_TYPE_MAPPING[1 << ev] 1i

778 except KeyError: 

779 raise ValueError(f"Unknown event type bit: {1 << ev}") 

780 events.append(ev_enum) 1i

781 return events 1i

782  

783 ########################################################################## 

784 # FAN 

785 # See external class definitions in _fan.pxi 

786  

787 def get_fan(self, fan: int = 0) -> FanInfo: 

788 """ 

789 :obj:`~_device.FanInfo` object to get information and manage a specific fan on a device. 

790 """ 

791 if fan < 0 or fan >= self.num_fans: 

792 raise ValueError(f"Fan index {fan} is out of range [0, {self.num_fans})") 

793 return FanInfo(self._handle, fan) 

794  

795 @property 

796 def num_fans(self) -> int: 

797 """ 

798 The number of fans on the device. 

799 """ 

800 return nvml.device_get_num_fans(self._handle) 1JM

801  

802 ########################################################################## 

803 # FIELD VALUES 

804 # See external class definitions in _field_values.pxi 

805  

806 def get_field_values(self, field_ids: list[int | tuple[int, int]]) -> FieldValues: 

807 """ 

808 Get multiple field values from the device. 

809  

810 Each value specified can raise its own exception. That exception will 

811 be raised when attempting to access the corresponding ``value`` from the 

812 returned :obj:`~_device.FieldValues` container. 

813  

814 To confirm that there are no exceptions in the entire container, call 

815 :meth:`~_device.FieldValues.validate`. 

816  

817 Parameters 

818 ---------- 

819 field_ids: list[int | tuple[int, int]] 

820 List of field IDs to query. 

821  

822 Each item may be either a single value from the :class:`FieldId` 

823 enum, or a pair of (:class:`FieldId`, scope ID). 

824  

825 Returns 

826 ------- 

827 :obj:`~_device.FieldValues` 

828 Container of field values corresponding to the requested field IDs. 

829 """ 

830 # Passing a field_ids array of length 0 raises an InvalidArgumentError, 

831 # so avoid that. 

832 if len(field_ids) == 0: 1lg30412

833 return FieldValues(nvml.FieldValue(0)) 1l

834  

835 return FieldValues(nvml.device_get_field_values(self._handle, field_ids)) 1lg30412

836  

837 def clear_field_values(self, field_ids: list[int | tuple[int, int]]) -> None: 

838 """ 

839 Clear multiple field values from the device. 

840  

841 Parameters 

842 ---------- 

843 field_ids: list[int | tuple[int, int]] 

844 List of field IDs to clear. 

845  

846 Each item may be either a single value from the :class:`FieldId` 

847 enum, or a pair of (:class:`FieldId`, scope ID). 

848 """ 

849 # Passing a field_ids array of length 0 raises an InvalidArgumentError, 

850 # so avoid that. 

851 if len(field_ids) == 0: 1l

852 return 

853  

854 nvml.device_clear_field_values(self._handle, field_ids) 1l

855  

856 ########################################################################## 

857 # INFOROM 

858 # See external class definitions in _inforom.pxi 

859  

860 @property 

861 def inforom(self) -> InforomInfo: 

862 """ 

863 :obj:`~_device.InforomInfo` object with InfoROM information. 

864  

865 For all products with an InfoROM. 

866 """ 

867 return InforomInfo(self) 1B

868  

869 ########################################################################## 

870 # MEMORY 

871 # See external class definitions in _memory.pxi 

872  

873 @property 

874 def bar1_memory_info(self) -> BAR1MemoryInfo: 

875 """ 

876 :obj:`~_device.BAR1MemoryInfo` object with BAR1 memory information. 

877  

878 BAR1 is used to map the FB (device memory) so that it can be directly 

879 accessed by the CPU or by 3rd party devices (peer-to-peer on the PCIE 

880 bus). 

881 """ 

882 return BAR1MemoryInfo(nvml.device_get_bar1_memory_info(self._handle)) 1x

883  

884 @property 

885 def memory_info(self) -> MemoryInfo: 

886 """ 

887 :obj:`~_device.MemoryInfo` object with memory information. 

888 """ 

889 return MemoryInfo(nvml.device_get_memory_info_v2(self._handle)) 1y

890  

891 ########################################################################## 

892 # NVLINK 

893 # See external class definitions in _nvlink.pxi 

894  

895 def get_nvlink(self, link: int) -> NvlinkInfo: 

896 """ 

897 Get :obj:`~NvlinkInfo` about this device. 

898  

899 For devices with NVLink support. 

900  

901 .. version-changed:: 1.1.0 

902 Any link number not supported by this specific device will raise a `ValueError`. 

903 """ 

904 link_count = self.get_nvlink_count() 1g

905 if link < 0 or link >= link_count: 1g

906 raise ValueError(f"Link index {link} is out of range [0, {link_count})") 

907 return NvlinkInfo(self, link) 1g

908  

909 def get_nvlink_count(self) -> int: 

910 """ 

911 Get the number of NVLink links on this device. 

912  

913 For devices with NVLink support. 

914  

915 .. version-added:: 1.1.0 

916 """ 

917 return self.get_field_values([FieldId.DEV_NVLINK_LINK_COUNT])[0].value 1g30412

918  

919 def get_nvlinks(self) -> Iterable[NvlinkInfo]: 

920 """ 

921 Get :obj:`~NvlinkInfo` about all NVLink links on this device. 

922  

923 For devices with NVLink support. 

924  

925 .. version-added:: 1.1.0 

926 """ 

927 for link in range(self.get_nvlink_count()): 1g012

928 yield self.get_nvlink(link) 1g

929  

930 ########################################################################## 

931 # PCI INFO 

932 # See external class definitions in _pci_info.pxi 

933  

934 @property 

935 def pci_info(self) -> PciInfo: 

936 """ 

937 :obj:`~_device.PciInfo` object with the PCI attributes of this device. 

938  

939 Non-physical devices, such as MIG devices, may not have PCI attributes. 

940 In that case, this property will raise a `RuntimeError`. 

941 """ 

942 try: 1kqejm

943 pci_info = nvml.device_get_pci_info_ext(self._handle) 1kqejm

944 except nvml.InvalidArgumentError: 

945 raise RuntimeError("This device does not have PCI attributes") from None 

946 else: 

947 return PciInfo(pci_info, self._handle) 1kqejm

948  

949 ########################################################################## 

950 # PERFORMANCE 

951 # See external class definitions in _performance.pxi 

952  

953 @property 

954 def performance_state(self) -> int | None: 

955 """ 

956 The current performance state of the device. 

957  

958 For Fermi™ or newer fully supported devices. 

959  

960 Returns 

961 ------- 

962 int | None 

963 The current performance state of the device, as an integer between 0 and 15, 

964 where 0 is maximum performance and higher numbers are lower performance. 

965 Returns `None` if the performance state is unknown. 

966 """ 

967 return _pstate_to_int(nvml.device_get_performance_state(self._handle)) 1fc

968  

969 @property 

970 def dynamic_pstates_info(self) -> GpuDynamicPstatesInfo: 

971 """ 

972 :obj:`~_device.GpuDynamicPstatesInfo` object with performance monitor samples from the associated subdevice. 

973 """ 

974 return GpuDynamicPstatesInfo(nvml.device_get_dynamic_pstates_info(self._handle)) 1c

975  

976 @property 

977 def supported_pstates(self) -> list[int]: 

978 """ 

979 Get all supported Performance States (P-States) for the device. 

980  

981 The returned list contains a contiguous list of valid P-States supported by 

982 the device. 

983  

984 Return 

985 ------ 

986 list[int] 

987 A list of supported performance state of the device, as an integer 

988 between 0 and 15, where 0 is maximum performance and higher numbers 

989 are lower performance. 

990 """ 

991 # From nvml.h: 

992 # The returned array would contain a contiguous list of valid P-States 

993 # supported by the device. If the number of supported P-States is fewer 

994 # than the size of the array supplied missing elements would contain \a 

995 # NVML_PSTATE_UNKNOWN. 

996  

997 pstates = [] 1c

998 for pstate in nvml.device_get_supported_performance_states(self._handle): 1c

999 pstate_value = _pstate_to_int(pstate) 1c

1000 if pstate_value is not None: 1c

1001 pstates.append(pstate_value) 1c

1002 return pstates 1c

1003  

1004 ########################################################################## 

1005 # PROCESS 

1006 # See external class definitions in _process.pxi 

1007  

1008 @property 

1009 def compute_running_processes(self) -> list[ProcessInfo]: 

1010 """ 

1011 Get information about processes with a compute context on a device 

1012  

1013 For Fermi™ or newer fully supported devices. 

1014  

1015 This function returns information only about compute running processes 

1016 (e.g. CUDA application which have active context). Any graphics 

1017 applications (e.g. using OpenGL, DirectX) won't be listed by this 

1018 function. 

1019  

1020 Keep in mind that information returned by this call is dynamic and the 

1021 number of elements might change in time. 

1022  

1023 In MIG mode, if device handle is provided, the API returns aggregate 

1024 information, only if the caller has appropriate privileges. Per-instance 

1025 information can be queried by using specific MIG device handles. 

1026 Querying per-instance information using MIG device handles is not 

1027 supported if the device is in vGPU Host virtualization mode. 

1028 """ 

1029 return [ProcessInfo(self, proc) for proc in nvml.device_get_compute_running_processes_v3(self._handle)] 1v

1030  

1031 ########################################################################## 

1032 # REPAIR STATUS 

1033 # See external class definitions in _repair_status.pxi 

1034  

1035 @property 

1036 def repair_status(self) -> RepairStatus: 

1037 """ 

1038 :obj:`~_device.RepairStatus` object with TPC/Channel repair status. 

1039  

1040 For Ampere™ or newer fully supported devices. 

1041 """ 

1042 return RepairStatus(self._handle) 1F

1043  

1044 ########################################################################## 

1045 # TEMPERATURE 

1046 # See external class definitions in _temperature.pxi 

1047  

1048 @property 

1049 def temperature(self) -> Temperature: 

1050 """ 

1051 :obj:`~_device.Temperature` object with temperature information for the device. 

1052 """ 

1053 return Temperature(self._handle) 1TUG

1054  

1055 ####################################################################### 

1056 # TOPOLOGY 

1057  

1058 def get_topology_nearest_gpus(self, level: GpuTopologyLevel | str) -> Iterable[Device]: 

1059 """ 

1060 Retrieve the GPUs that are nearest to this device at a specific interconnectivity level. 

1061  

1062 Supported on Linux only. 

1063  

1064 Parameters 

1065 ---------- 

1066 level: :class:`GpuTopologyLevel` 

1067 The topology level. 

1068  

1069 Returns 

1070 ------- 

1071 Iterable of :class:`Device` 

1072 The nearest devices at the given topology level. 

1073 """ 

1074 cdef Device device 

1075 try: 1ar

1076 level = _GPU_TOPOLOGY_LEVEL_MAPPING[level] 1ar

1077 except KeyError: 1a

1078 raise ValueError( 1a

1079 f"Invalid topology level: {level}. " 1a

1080 f"Must be one of {list(GpuTopologyLevel.__members__.values())}" 1a

1081 ) from None 1a

1082 for handle in nvml.device_get_topology_nearest_gpus(self._handle, level): 1r

1083 device = Device.__new__(Device) 

1084 device._handle = handle 

1085 yield device 

1086  

1087 ####################################################################### 

1088 # UTILIZATION 

1089  

1090 @property 

1091 def utilization(self) -> Utilization: 

1092 """ 

1093 Retrieves the current :obj:`~Utilization` rates for the device's major 

1094 subsystems. 

1095  

1096 For Fermi™ or newer fully supported devices. 

1097  

1098 Note: During driver initialization when ECC is enabled one can see high 

1099 GPU and Memory Utilization readings. This is caused by ECC Memory 

1100 Scrubbing mechanism that is performed during driver initialization. 

1101  

1102 Note: On MIG-enabled GPUs, querying device utilization rates is not 

1103 currently supported. 

1104  

1105 Returns 

1106 ------- 

1107 Utilization 

1108 An object containing the current utilization rates for the device. 

1109 """ 

1110 return Utilization(nvml.device_get_utilization_rates(self._handle)) 1H

1111  

1112  

1113def get_topology_common_ancestor(Device device1 not None: Device, Device device2 not None: Device) -> GpuTopologyLevel: 

1114 """ 

1115 Retrieve the common ancestor for two devices. 

1116  

1117 For Linux only. 

1118  

1119 Parameters 

1120 ---------- 

1121 device1: :class:`Device` 

1122 The first device. 

1123 device2: :class:`Device` 

1124 The second device. 

1125  

1126 Returns 

1127 ------- 

1128 :class:`GpuTopologyLevel` 

1129 The common ancestor level of the two devices. 

1130 """ 

1131 return _GPU_TOPOLOGY_LEVEL_INV_MAPPING[ 

1132 nvml.device_get_topology_common_ancestor( 

1133 device1._handle, 

1134 device2._handle, 

1135 ) 

1136 ] 

1137  

1138  

1139def get_p2p_status(Device device1 not None: Device, Device device2 not None: Device, index: GpuP2PCapsIndex | str) -> GpuP2PStatus: 

1140 """ 

1141 Retrieve the P2P status between two devices. 

1142  

1143 Parameters 

1144 ---------- 

1145 device1: :class:`Device` 

1146 The first device. 

1147 device2: :class:`Device` 

1148 The second device. 

1149 index: :class:`GpuP2PCapsIndex` | str 

1150 The P2P capability index being looked for between ``device1`` and ``device2``. 

1151  

1152 Returns 

1153 ------- 

1154 :class:`GpuP2PStatus` 

1155 The P2P status between the two devices. 

1156 """ 

1157 try: 1a

1158 index_enum = _GPU_P2P_CAPS_INDEX_MAPPING[index] 1a

1159 except KeyError: 1a

1160 raise ValueError( 1a

1161 f"Invalid P2P caps index: {index}. " 1a

1162 f"Must be one of {list(GpuP2PCapsIndex.__members__.values())}" 1a

1163 ) from None 1a

1164 return _GPU_P2P_STATUS_MAPPING.get( 

1165 nvml.device_get_p2p_status( 

1166 device1._handle, 

1167 device2._handle, 

1168 index_enum, 

1169 ), 

1170 GpuP2PStatus.UNKNOWN 

1171 ) 

1172  

1173  

1174__all__ = [ 

1175 "Device", 

1176 "get_p2p_status", 

1177 "get_topology_common_ancestor", 

1178 "NvlinkInfo", 

1179]