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

262 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-03 02:41 +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: 1afd

45 return None 1ad

46 assert ( 1afd

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

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

50  

51  

52cdef int _pstate_to_enum(int pstate): 

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

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} 

109  

110  

111_GPU_P2P_CAPS_INDEX_MAPPING = { 

112 GpuP2PCapsIndex.READ: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_READ, 

113 GpuP2PCapsIndex.WRITE: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_WRITE, 

114 GpuP2PCapsIndex.NVLINK: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_NVLINK, 

115 GpuP2PCapsIndex.ATOMICS: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_ATOMICS, 

116 GpuP2PCapsIndex.PCI: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_PCI, 

117 GpuP2PCapsIndex.PROP: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_PROP, 

118 GpuP2PCapsIndex.UNKNOWN: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_UNKNOWN, 

119} 

120  

121  

122_GPU_P2P_STATUS_MAPPING = { 

123 nvml.GpuP2PStatus.P2P_STATUS_OK: GpuP2PStatus.OK, 

124 nvml.GpuP2PStatus.P2P_STATUS_CHIPSET_NOT_SUPPORTED: GpuP2PStatus.CHIPSET_NOT_SUPPORTED, 

125 nvml.GpuP2PStatus.P2P_STATUS_GPU_NOT_SUPPORTED: GpuP2PStatus.GPU_NOT_SUPPORTED, 

126 nvml.GpuP2PStatus.P2P_STATUS_IOH_TOPOLOGY_NOT_SUPPORTED: GpuP2PStatus.IOH_TOPOLOGY_NOT_SUPPORTED, 

127 nvml.GpuP2PStatus.P2P_STATUS_DISABLED_BY_REGKEY: GpuP2PStatus.DISABLED_BY_REGKEY, 

128 nvml.GpuP2PStatus.P2P_STATUS_NOT_SUPPORTED: GpuP2PStatus.NOT_SUPPORTED, 

129 nvml.GpuP2PStatus.P2P_STATUS_UNKNOWN: GpuP2PStatus.UNKNOWN, 

130} 

131  

132  

133_GPU_TOPOLOGY_LEVEL_MAPPING = { 

134 GpuTopologyLevel.INTERNAL: nvml.GpuTopologyLevel.TOPOLOGY_INTERNAL, 

135 GpuTopologyLevel.SINGLE: nvml.GpuTopologyLevel.TOPOLOGY_SINGLE, 

136 GpuTopologyLevel.MULTIPLE: nvml.GpuTopologyLevel.TOPOLOGY_MULTIPLE, 

137 GpuTopologyLevel.HOSTBRIDGE: nvml.GpuTopologyLevel.TOPOLOGY_HOSTBRIDGE, 

138 GpuTopologyLevel.NODE: nvml.GpuTopologyLevel.TOPOLOGY_NODE, 

139 GpuTopologyLevel.SYSTEM: nvml.GpuTopologyLevel.TOPOLOGY_SYSTEM, 

140} 

141  

142  

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

144  

145  

146  

147cdef class Device: 

148 """ 

149 Representation of a device. 

150  

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

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

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

154  

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

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

157  

158 Parameters 

159 ---------- 

160 index: int, optional 

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

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

163  

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

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

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

167  

168 uuid: bytes or str, optional 

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

170  

171 pci_bus_id: bytes or str, optional 

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

173  

174 Raises 

175 ------ 

176 ValueError 

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

178 """ 

179  

180 # This is made public for testing purposes only 

181 cdef public intptr_t _handle 

182  

183 def __init__( 

184 self, 

185 *, 

186 index: int | None = None, 

187 uuid: bytes | str | None = None, 

188 pci_bus_id: bytes | str | None = None, 

189 ) -> None: 

190 args = [index, uuid, pci_bus_id] 1sctuefvwpbxyLViRzMloANBCmDOqPEFGgndjHTUIkJQhSr

191 cdef int arg_count = sum(arg is not None for arg in args) 1sctuefvwpbxyLViRzMloANBCmDOqPEFGgndjHTUIkJQhSr

192  

193 if arg_count > 1: 1sctuefvwpbxyLViRzMloANBCmDOqPEFGgndjHTUIkJQhSr

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

195 if arg_count == 0: 1sctuefvwpbxyLViRzMloANBCmDOqPEFGgndjHTUIkJQhSr

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

197  

198 initialize() 1sctuefvwpbxyLiRzMloANBCmDOqPEFGgndjHTUIkJQhSr

199  

200 if index is not None: 1sctuefvwpbxyLiRzMloANBCmDOqPEFGgndjHTUIkJQhSr

201 self._handle = nvml.device_get_handle_by_index_v2(index) 1sctuefwpbxyLizMoANBCmDOqPEFGgndjHTUIkJQh

202 elif uuid is not None: 1vRlhSr

203 if isinstance(uuid, bytes): 1vRlSr

204 uuid = uuid.decode("ascii") 

205 self._handle = nvml.device_get_handle_by_uuid(uuid) 1vRlSr

206 elif pci_bus_id is not None: 1lh

207 if isinstance(pci_bus_id, bytes): 1lh

208 pci_bus_id = pci_bus_id.decode("ascii") 

209 self._handle = nvml.device_get_handle_by_pci_bus_id_v2(pci_bus_id) 1lh

210  

211 ######################################################################### 

212 # BASIC PROPERTIES 

213  

214 @property 

215 def index(self) -> int: 

216 """ 

217 The NVML index of this device. 

218  

219 Valid indices are derived from the count returned by 

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

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

222 1. 

223  

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

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

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

227  

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

229 device index. 

230 """ 

231 return nvml.device_get_index(self._handle) 1sctuefvwxyizloACmKDPEFGgndHIJh

232  

233 @property 

234 def uuid(self) -> str: 

235 """ 

236 Retrieves the globally unique immutable UUID associated with this 

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

238 board serial identifier. 

239  

240 In the upstream NVML C++ API, the UUID includes a ``gpu-`` or ``mig-`` 

241 prefix. If you need a `uuid` without that prefix (for example, to 

242 interact with CUDA), use the `uuid_without_prefix` property. 

243 """ 

244 return nvml.device_get_uuid(self._handle) 1Q

245  

246 @property 

247 def uuid_without_prefix(self) -> str: 

248 """ 

249 Retrieves the globally unique immutable UUID associated with this 

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

251 board serial identifier. 

252  

253 In the upstream NVML C++ API, the UUID includes a ``gpu-`` or ``mig-`` 

254 prefix. This property returns it without the prefix, to match the UUIDs 

255 used in CUDA. If you need the prefix, use the `uuid` property. 

256 """ 

257 # NVML UUIDs have a `gpu-` or `mig-` prefix. We remove that here. 

258 return nvml.device_get_uuid(self._handle)[4:] 1Nkr

259  

260 @property 

261 def pci_bus_id(self) -> str: 

262 """ 

263 Retrieves the PCI bus ID of this device. 

264 """ 

265 return self.pci_info.bus_id 1h

266  

267 @property 

268 def numa_node_id(self) -> int: 

269 """ 

270 The NUMA node of the given GPU device. 

271  

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

273 """ 

274 return nvml.device_get_numa_node_id(self._handle) 1G

275  

276 @property 

277 def arch(self) -> DeviceArch: 

278 """ 

279 :obj:`~DeviceArch` device architecture. 

280  

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

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

283 "AMPERE"``. 

284 """ 

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

286 try: 1p

287 return DeviceArch(arch) 1p

288 except ValueError: 

289 return DeviceArch.UNKNOWN 

290  

291 @property 

292 def name(self) -> str: 

293 """ 

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

295 """ 

296 return nvml.device_get_name(self._handle) 1M

297  

298 @property 

299 def brand(self) -> str: 

300 """ 

301 The brand of the device. 

302  

303 Returns "Unknown" if the brand is unknown. 

304 """ 

305 return _BRAND_TYPE_MAPPING.get(nvml.device_get_brand(self._handle), "Unknown") 1L

306  

307 @property 

308 def serial(self) -> str: 

309 """ 

310 Retrieves the globally unique board serial number associated with this 

311 device's board. 

312  

313 For all products with an InfoROM. 

314 """ 

315 return nvml.device_get_serial(self._handle) 1A

316  

317 @property 

318 def module_id(self) -> int: 

319 """ 

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

321  

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

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

324 be 0. 

325 """ 

326 return nvml.device_get_module_id(self._handle) 1F

327  

328 @property 

329 def minor_number(self) -> int: 

330 """ 

331 The minor number of this device. 

332  

333 For Linux only. 

334  

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

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

337 """ 

338 return nvml.device_get_minor_number(self._handle) 1O

339  

340 @property 

341 def is_c2c_enabled(self) -> bool: 

342 """ 

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

344 """ 

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

346  

347 @property 

348 def is_persistence_mode_enabled(self) -> bool: 

349 """ 

350 Whether persistence mode is enabled for this device. 

351  

352 For Linux only. 

353 """ 

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

355  

356 @is_persistence_mode_enabled.setter 

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

358 nvml.device_set_persistence_mode( 1n

359 self._handle, 1n

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

361 ) 

362  

363 @property 

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

365 """ 

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

367  

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

369 """ 

370 return nvml.device_get_cuda_compute_capability(self._handle) 1R

371  

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

373 """ 

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

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

376 NVIDIA Management Library (NVML) access). 

377  

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

379  

380 Returns 

381 ------- 

382 cuda.core.Device 

383 The corresponding CUDA device. 

384  

385 Raises 

386 ------ 

387 RuntimeError 

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

389  

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

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

392 though it can be enumerated from NVML. 

393 """ 

394 from cuda.core import Device as CudaDevice 1k

395  

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

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

398  

399 for cuda_device in CudaDevice.get_all_devices(): 1k

400 if cuda_device.uuid == self.uuid_without_prefix: 1k

401 return cuda_device 1k

402  

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

404  

405 @classmethod 

406 def get_device_count(cls) -> int: 

407 """ 

408 Get the number of available devices. 

409  

410 Returns 

411 ------- 

412 int 

413 The number of available devices. 

414 """ 

415 initialize() 1WXY

416  

417 return nvml.device_get_count_v2() 1WXY

418  

419 @classmethod 

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

421 """ 

422 Query the available device instances. 

423  

424 Returns 

425 ------- 

426 Iterator over :obj:`~Device` 

427 An iterator over available devices. 

428 """ 

429 initialize() 1sctuefwpxyLizMoANBCmDOqPEFGgndjHIkJQh

430  

431 for device_id in range(nvml.device_get_count_v2()): 1sctuefwpxyLizMoANBCmDOqPEFGgndjHIkJQh

432 yield cls(index=device_id) 1sctuefwpxyLizMoANBCmDOqPEFGgndjHIkJQh

433  

434 ######################################################################### 

435 # ADDRESSING MODE 

436  

437 @property 

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

439 """ 

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

441 """ 

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

443  

444 ######################################################################### 

445 # MIG (MULTI-INSTANCE GPU) DEVICES 

446  

447 @property 

448 def mig(self) -> MigInfo: 

449 """ 

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

451  

452 For Ampere™ or newer fully supported devices. 

453 """ 

454 return MigInfo(self) 1vE

455  

456 ######################################################################### 

457 # AFFINITY 

458  

459 @classmethod 

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

461 """ 

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

463  

464 Supported on Linux only. 

465  

466 Parameters 

467 ---------- 

468 cpu_index: int 

469 The CPU index. 

470  

471 Returns 

472 ------- 

473 Iterator of :obj:`~Device` 

474 An iterator over available devices. 

475 """ 

476 cdef Device device 

477 for handle in nvml.system_get_topology_gpu_set(cpu_index): 1K

478 device = Device.__new__(Device) 1K

479 device._handle = handle 1K

480 yield device 1K

481  

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

483 """ 

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

485 memory affinity for the device. 

486  

487 For Kepler™ or newer fully supported devices. 

488  

489 Supported on Linux only. 

490  

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

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

493 ancestor of the device. 

494  

495 Parameters 

496 ---------- 

497 scope: AffinityScope | str, optional 

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

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

500  

501 Returns 

502 ------- 

503 list[int] 

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

505 affinity for the device. 

506 """ 

507 try: 1cb

508 scope = _AFFINITY_SCOPE_MAPPING[scope] 1cb

509 except KeyError: 1b

510 raise ValueError( 1b

511 f"Invalid affinity scope: {scope}. " 1b

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

513 ) from None 1b

514 return _unpack_bitmask( 1c

515 nvml.device_get_memory_affinity( 1c

516 self._handle, 1c

517 <unsigned int>ceil(cpu_count() / 64), 1c

518 scope, 1c

519 ) 

520 ) 

521  

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

523 """ 

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

525 CPU affinity for the device. 

526  

527 For Kepler™ or newer fully supported devices. 

528  

529 Supported on Linux only. 

530  

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

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

533 ancestor of the device. 

534  

535 Parameters 

536 ---------- 

537 scope: AffinityScope | str, optional 

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

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

540  

541 Returns 

542 ------- 

543 list[int] 

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

545 affinity for the device. 

546 """ 

547 try: 1cbiK

548 scope = _AFFINITY_SCOPE_MAPPING[scope] 1cbiK

549 except KeyError: 1b

550 raise ValueError( 1b

551 f"Invalid affinity scope: {scope}. " 1b

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

553 ) from None 1b

554 return _unpack_bitmask( 1ciK

555 nvml.device_get_cpu_affinity_within_scope( 1ciK

556 self._handle, 1ciK

557 <unsigned int>ceil(cpu_count() / 64), 1ciK

558 scope, 1ciK

559 ) 

560 ) 

561  

562 def set_cpu_affinity(self) -> None: 

563 """ 

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

565  

566 For Kepler™ or newer fully supported devices. 

567  

568 Supported on Linux only. 

569 """ 

570 nvml.device_set_cpu_affinity(self._handle) 

571  

572 def clear_cpu_affinity(self) -> None: 

573 """ 

574 Clear all affinity bindings for the calling thread. 

575  

576 For Kepler™ or newer fully supported devices. 

577  

578 Supported on Linux only. 

579 """ 

580 nvml.device_clear_cpu_affinity(self._handle) 

581  

582 ######################################################################### 

583 # CLOCK 

584 # See external class definitions in _clock.pxi 

585  

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

587 """ 

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

589 """ 

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

591  

592 @property 

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

594 """ 

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

596  

597 For Kepler™ or newer fully supported devices. 

598  

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

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

601 limits allow. 

602  

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

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

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

606  

607 Returns 

608 ------- 

609 bool 

610 The current state of Auto Boosted clocks 

611 bool 

612 The default Auto Boosted clocks behavior 

613  

614 """ 

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

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

617  

618 @property 

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

620 """ 

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

622  

623 For all fully supported products. 

624 """ 

625 cdef uint64_t[1] reasons 

626 reasons[0] = nvml.device_get_current_clocks_event_reasons(self._handle) 1e

627 output_reasons = [] 1e

628 for reason in _unpack_bitmask(reasons): 1e

629 try: 

630 output_reason = _CLOCKS_EVENT_REASONS_MAPPING[1 << reason] 

631 except KeyError: 

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

633 output_reasons.append(output_reason) 

634 return output_reasons 1e

635  

636 @property 

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

638 """ 

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

640 :meth:`get_current_clock_event_reasons`. 

641  

642 For all fully supported products. 

643  

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

645 """ 

646 cdef uint64_t[1] reasons 

647 reasons[0] = nvml.device_get_supported_clocks_event_reasons(self._handle) 1e

648 output_reasons = [] 1e

649 for reason in _unpack_bitmask(reasons): 1e

650 try: 1e

651 output_reason = _CLOCKS_EVENT_REASONS_MAPPING[1 << reason] 1e

652 except KeyError: 

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

654 output_reasons.append(output_reason) 1e

655 return output_reasons 1e

656  

657 ########################################################################## 

658 # COOLER 

659 # See external class definitions in _cooler.pxi 

660  

661 @property 

662 def cooler(self) -> CoolerInfo: 

663 """ 

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

665 """ 

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

667  

668 ########################################################################## 

669 # DEVICE ATTRIBUTES 

670 # See external class definitions in _device_attributes.pxi 

671  

672 @property 

673 def attributes(self) -> DeviceAttributes: 

674 """ 

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

676  

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

678 systems. 

679 """ 

680 return DeviceAttributes(nvml.device_get_attributes_v2(self._handle)) 1x

681  

682 ######################################################################### 

683 # DISPLAY 

684  

685 @property 

686 def is_display_connected(self) -> bool: 

687 """ 

688 The display mode for this device. 

689  

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

691 any of the device's connectors. 

692 """ 

693 return nvml.device_get_display_mode(self._handle) == nvml.EnableState.FEATURE_ENABLED 1B

694  

695 @property 

696 def is_display_active(self) -> bool: 

697 """ 

698 The display active status for this device. 

699  

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

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

702 the screen. 

703  

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

705 """ 

706 return nvml.device_get_display_active(self._handle) == nvml.EnableState.FEATURE_ENABLED 1B

707  

708 ########################################################################## 

709 # EVENTS 

710 # See external class definitions in _event.pxi 

711  

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

713 """ 

714 Starts recording events on this device. 

715  

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

717  

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

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

720 available only on Power Management enabled devices (see 

721 :meth:`Device.get_power_management_mode`). 

722  

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

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

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

726  

727 Examples 

728 -------- 

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

730 >>> events = device.register_events([ 

731 ... EventType.XID_CRITICAL_ERROR, 

732 ... ]) 

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

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

735  

736 Parameters 

737 ---------- 

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

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

740  

741 Returns 

742 ------- 

743 :obj:`~_device.DeviceEvents` 

744 An object representing the registered events. Call 

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

746  

747 Raises 

748 ------ 

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

750 None of the requested event types are registered. 

751 """ 

752 return DeviceEvents(self._handle, events) 1j

753  

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

755 """ 

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

757  

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

759 empty list on Windows). 

760  

761 Returns 

762 ------- 

763 list[EventType] 

764 The list of supported event types. 

765 """ 

766 cdef uint64_t[1] bitmask 

767 bitmask[0] = nvml.device_get_supported_event_types(self._handle) 1j

768 events = [] 1j

769 for ev in _unpack_bitmask(bitmask): 1j

770 try: 1j

771 ev_enum = _EVENT_TYPE_MAPPING[1 << ev] 1j

772 except KeyError: 

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

774 events.append(ev_enum) 1j

775 return events 1j

776  

777 ########################################################################## 

778 # FAN 

779 # See external class definitions in _fan.pxi 

780  

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

782 """ 

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

784 """ 

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

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

787 return FanInfo(self._handle, fan) 

788  

789 @property 

790 def num_fans(self) -> int: 

791 """ 

792 The number of fans on the device. 

793 """ 

794 return nvml.device_get_num_fans(self._handle) 1wC

795  

796 ########################################################################## 

797 # FIELD VALUES 

798 # See external class definitions in _field_values.pxi 

799  

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

801 """ 

802 Get multiple field values from the device. 

803  

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

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

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

807  

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

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

810  

811 Parameters 

812 ---------- 

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

814 List of field IDs to query. 

815  

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

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

818  

819 Returns 

820 ------- 

821 :obj:`~_device.FieldValues` 

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

823 """ 

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

825 # so avoid that. 

826 if len(field_ids) == 0: 1mg

827 return FieldValues(nvml.FieldValue(0)) 1m

828  

829 return FieldValues(nvml.device_get_field_values(self._handle, field_ids)) 1mg

830  

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

832 """ 

833 Clear multiple field values from the device. 

834  

835 Parameters 

836 ---------- 

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

838 List of field IDs to clear. 

839  

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

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

842 """ 

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

844 # so avoid that. 

845 if len(field_ids) == 0: 1m

846 return 

847  

848 nvml.device_clear_field_values(self._handle, field_ids) 1m

849  

850 ########################################################################## 

851 # INFOROM 

852 # See external class definitions in _inforom.pxi 

853  

854 @property 

855 def inforom(self) -> InforomInfo: 

856 """ 

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

858  

859 For all products with an InfoROM. 

860 """ 

861 return InforomInfo(self) 1D

862  

863 ########################################################################## 

864 # MEMORY 

865 # See external class definitions in _memory.pxi 

866  

867 @property 

868 def bar1_memory_info(self) -> BAR1MemoryInfo: 

869 """ 

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

871  

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

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

874 bus). 

875 """ 

876 return BAR1MemoryInfo(nvml.device_get_bar1_memory_info(self._handle)) 1y

877  

878 @property 

879 def memory_info(self) -> MemoryInfo: 

880 """ 

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

882 """ 

883 return MemoryInfo(nvml.device_get_memory_info_v2(self._handle)) 1z

884  

885 ########################################################################## 

886 # NVLINK 

887 # See external class definitions in _nvlink.pxi 

888  

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

890 """ 

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

892  

893 For devices with NVLink support. 

894  

895 .. version-changed:: 1.1.0 

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

897 """ 

898 link_count = self.get_nvlink_count() 1g

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

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

901 return NvlinkInfo(self, link) 1g

902  

903 def get_nvlink_count(self) -> int: 

904 """ 

905 Get the number of NVLink links on this device. 

906  

907 For devices with NVLink support. 

908  

909 .. version-added:: 1.1.0 

910 """ 

911 return self.get_field_values([FieldId.DEV_NVLINK_LINK_COUNT])[0].value 1g

912  

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

914 """ 

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

916  

917 For devices with NVLink support. 

918  

919 .. version-added:: 1.1.0 

920 """ 

921 for link in range(self.get_nvlink_count()): 1g

922 yield self.get_nvlink(link) 1g

923  

924 ########################################################################## 

925 # PCI INFO 

926 # See external class definitions in _pci_info.pxi 

927  

928 @property 

929 def pci_info(self) -> PciInfo: 

930 """ 

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

932  

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

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

935 """ 

936 try: 1lokhr

937 pci_info = nvml.device_get_pci_info_ext(self._handle) 1lokhr

938 except nvml.InvalidArgumentError: 

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

940 else: 

941 return PciInfo(pci_info, self._handle) 1lokhr

942  

943 ########################################################################## 

944 # PERFORMANCE 

945 # See external class definitions in _performance.pxi 

946  

947 @property 

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

949 """ 

950 The current performance state of the device. 

951  

952 For Fermi™ or newer fully supported devices. 

953  

954 Returns 

955 ------- 

956 int | None 

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

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

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

960 """ 

961 return _pstate_to_int(nvml.device_get_performance_state(self._handle)) 1fd

962  

963 @property 

964 def dynamic_pstates_info(self) -> GpuDynamicPstatesInfo: 

965 """ 

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

967 """ 

968 return GpuDynamicPstatesInfo(nvml.device_get_dynamic_pstates_info(self._handle)) 1d

969  

970 @property 

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

972 """ 

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

974  

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

976 the device. 

977  

978 Return 

979 ------ 

980 list[int] 

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

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

983 are lower performance. 

984 """ 

985 # From nvml.h: 

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

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

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

989 # NVML_PSTATE_UNKNOWN. 

990  

991 pstates = [] 1d

992 for pstate in nvml.device_get_supported_performance_states(self._handle): 1d

993 pstate_value = _pstate_to_int(pstate) 1d

994 if pstate_value is not None: 1d

995 pstates.append(pstate_value) 1d

996 return pstates 1d

997  

998 ########################################################################## 

999 # PROCESS 

1000 # See external class definitions in _process.pxi 

1001  

1002 @property 

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

1004 """ 

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

1006  

1007 For Fermi™ or newer fully supported devices. 

1008  

1009 This function returns information only about compute running processes 

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

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

1012 function. 

1013  

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

1015 number of elements might change in time. 

1016  

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

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

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

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

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

1022 """ 

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

1024  

1025 ########################################################################## 

1026 # REPAIR STATUS 

1027 # See external class definitions in _repair_status.pxi 

1028  

1029 @property 

1030 def repair_status(self) -> RepairStatus: 

1031 """ 

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

1033  

1034 For Ampere™ or newer fully supported devices. 

1035 """ 

1036 return RepairStatus(self._handle) 1H

1037  

1038 ########################################################################## 

1039 # TEMPERATURE 

1040 # See external class definitions in _temperature.pxi 

1041  

1042 @property 

1043 def temperature(self) -> Temperature: 

1044 """ 

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

1046 """ 

1047 return Temperature(self._handle) 1TUI

1048  

1049 ####################################################################### 

1050 # TOPOLOGY 

1051  

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

1053 """ 

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

1055  

1056 Supported on Linux only. 

1057  

1058 Parameters 

1059 ---------- 

1060 level: :class:`GpuTopologyLevel` 

1061 The topology level. 

1062  

1063 Returns 

1064 ------- 

1065 Iterable of :class:`Device` 

1066 The nearest devices at the given topology level. 

1067 """ 

1068 cdef Device device 

1069 try: 1bq

1070 level = _GPU_TOPOLOGY_LEVEL_MAPPING[level] 1bq

1071 except KeyError: 1b

1072 raise ValueError( 1b

1073 f"Invalid topology level: {level}. " 1b

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

1075 ) from None 1b

1076 for handle in nvml.device_get_topology_nearest_gpus(self._handle, level): 1q

1077 device = Device.__new__(Device) 

1078 device._handle = handle 

1079 yield device 

1080  

1081 ####################################################################### 

1082 # UTILIZATION 

1083  

1084 @property 

1085 def utilization(self) -> Utilization: 

1086 """ 

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

1088 subsystems. 

1089  

1090 For Fermi™ or newer fully supported devices. 

1091  

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

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

1094 Scrubbing mechanism that is performed during driver initialization. 

1095  

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

1097 currently supported. 

1098  

1099 Returns 

1100 ------- 

1101 Utilization 

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

1103 """ 

1104 return Utilization(nvml.device_get_utilization_rates(self._handle)) 1J

1105  

1106  

1107def get_topology_common_ancestor(device1: Device, device2: Device) -> GpuTopologyLevel: 

1108 """ 

1109 Retrieve the common ancestor for two devices. 

1110  

1111 For Linux only. 

1112  

1113 Parameters 

1114 ---------- 

1115 device1: :class:`Device` 

1116 The first device. 

1117 device2: :class:`Device` 

1118 The second device. 

1119  

1120 Returns 

1121 ------- 

1122 :class:`GpuTopologyLevel` 

1123 The common ancestor level of the two devices. 

1124 """ 

1125 return _GPU_TOPOLOGY_LEVEL_INV_MAPPING[ 

1126 nvml.device_get_topology_common_ancestor( 

1127 device1._handle, 

1128 device2._handle, 

1129 ) 

1130 ] 

1131  

1132  

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

1134 """ 

1135 Retrieve the P2P status between two devices. 

1136  

1137 Parameters 

1138 ---------- 

1139 device1: :class:`Device` 

1140 The first device. 

1141 device2: :class:`Device` 

1142 The second device. 

1143 index: :class:`GpuP2PCapsIndex` | str 

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

1145  

1146 Returns 

1147 ------- 

1148 :class:`GpuP2PStatus` 

1149 The P2P status between the two devices. 

1150 """ 

1151 try: 1b

1152 index_enum = _GPU_P2P_CAPS_INDEX_MAPPING[index] 1b

1153 except KeyError: 1b

1154 raise ValueError( 1b

1155 f"Invalid P2P caps index: {index}. " 1b

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

1157 ) from None 1b

1158 return _GPU_P2P_STATUS_MAPPING.get( 

1159 nvml.device_get_p2p_status( 

1160 device1._handle, 

1161 device2._handle, 

1162 index_enum, 

1163 ), 

1164 GpuP2PStatus.UNKNOWN 

1165 ) 

1166  

1167  

1168__all__ = [ 

1169 "Device", 

1170 "get_p2p_status", 

1171 "get_topology_common_ancestor", 

1172 "NvlinkInfo", 

1173]