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

267 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-05 01:11 +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: 1gd

45 return None 1d

46 if int(pstate) < 0 or int(pstate) > 15: 1gd

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

48 return int(pstate) - int(nvml.Pstates.PSTATE_0) 1gd

49  

50  

51cdef int _pstate_to_enum(int pstate): 

52 if pstate < 0 or pstate > 15: 1g

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

54 return int(pstate) + int(nvml.Pstates.PSTATE_0) 1g

55  

56  

57include "_clock.pxi" 

58include "_cooler.pxi" 

59include "_device_attributes.pxi" 

60include "_device_utils.pxi" 

61include "_event.pxi" 

62include "_fan.pxi" 

63include "_field_values.pxi" 

64include "_inforom.pxi" 

65include "_memory.pxi" 

66include "_mig.pxi" 

67include "_nvlink.pxi" 

68include "_pci_info.pxi" 

69include "_performance.pxi" 

70include "_process.pxi" 

71include "_repair_status.pxi" 

72include "_temperature.pxi" 

73include "_utilization.pxi" 

74  

75  

76_ADDRESSING_MODE_MAPPING = { 

77 nvml.DeviceAddressingModeType.DEVICE_ADDRESSING_MODE_HMM: AddressingMode.HMM, 

78 nvml.DeviceAddressingModeType.DEVICE_ADDRESSING_MODE_ATS: AddressingMode.ATS, 

79} 

80  

81  

82_AFFINITY_SCOPE_MAPPING = { 

83 AffinityScope.NODE: nvml.AffinityScope.NODE, 

84 AffinityScope.SOCKET: nvml.AffinityScope.SOCKET, 

85} 

86  

87  

88_BRAND_TYPE_MAPPING = { 

89 nvml.BrandType.BRAND_UNKNOWN: "Unknown", 

90 nvml.BrandType.BRAND_QUADRO: "Quadro", 

91 nvml.BrandType.BRAND_TESLA: "Tesla", 

92 nvml.BrandType.BRAND_NVS: "NVS", 

93 nvml.BrandType.BRAND_GRID: "GRID", 

94 nvml.BrandType.BRAND_GEFORCE: "GeForce", 

95 nvml.BrandType.BRAND_TITAN: "Titan", 

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

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

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

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

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

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

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

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

104 nvml.BrandType.BRAND_NVIDIA: "NVIDIA", 

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

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

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

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

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

110} 

111  

112  

113_GPU_P2P_CAPS_INDEX_MAPPING = { 

114 GpuP2PCapsIndex.READ: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_READ, 

115 GpuP2PCapsIndex.WRITE: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_WRITE, 

116 GpuP2PCapsIndex.NVLINK: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_NVLINK, 

117 GpuP2PCapsIndex.ATOMICS: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_ATOMICS, 

118 GpuP2PCapsIndex.PCI: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_PCI, 

119 GpuP2PCapsIndex.PROP: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_PROP, 

120 GpuP2PCapsIndex.UNKNOWN: nvml.GpuP2PCapsIndex.P2P_CAPS_INDEX_UNKNOWN, 

121} 

122  

123  

124_GPU_P2P_STATUS_MAPPING = { 

125 nvml.GpuP2PStatus.P2P_STATUS_OK: GpuP2PStatus.OK, 

126 nvml.GpuP2PStatus.P2P_STATUS_CHIPSET_NOT_SUPPORTED: GpuP2PStatus.CHIPSET_NOT_SUPPORTED, 

127 nvml.GpuP2PStatus.P2P_STATUS_GPU_NOT_SUPPORTED: GpuP2PStatus.GPU_NOT_SUPPORTED, 

128 nvml.GpuP2PStatus.P2P_STATUS_IOH_TOPOLOGY_NOT_SUPPORTED: GpuP2PStatus.IOH_TOPOLOGY_NOT_SUPPORTED, 

129 nvml.GpuP2PStatus.P2P_STATUS_DISABLED_BY_REGKEY: GpuP2PStatus.DISABLED_BY_REGKEY, 

130 nvml.GpuP2PStatus.P2P_STATUS_NOT_SUPPORTED: GpuP2PStatus.NOT_SUPPORTED, 

131 nvml.GpuP2PStatus.P2P_STATUS_UNKNOWN: GpuP2PStatus.UNKNOWN, 

132} 

133  

134  

135_GPU_TOPOLOGY_LEVEL_MAPPING = { 

136 GpuTopologyLevel.INTERNAL: nvml.GpuTopologyLevel.TOPOLOGY_INTERNAL, 

137 GpuTopologyLevel.SINGLE: nvml.GpuTopologyLevel.TOPOLOGY_SINGLE, 

138 GpuTopologyLevel.MULTIPLE: nvml.GpuTopologyLevel.TOPOLOGY_MULTIPLE, 

139 GpuTopologyLevel.HOSTBRIDGE: nvml.GpuTopologyLevel.TOPOLOGY_HOSTBRIDGE, 

140 GpuTopologyLevel.NODE: nvml.GpuTopologyLevel.TOPOLOGY_NODE, 

141 GpuTopologyLevel.SYSTEM: nvml.GpuTopologyLevel.TOPOLOGY_SYSTEM, 

142} 

143  

144  

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

146  

147  

148cdef class Device: 

149 """ 

150 Representation of a device. 

151  

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

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

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

155  

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

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

158  

159 Parameters 

160 ---------- 

161 index: int, optional 

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

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

164  

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

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

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

168  

169 uuid: bytes or str, optional 

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

171  

172 pci_bus_id: bytes or str, optional 

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

174  

175 Raises 

176 ------ 

177 ValueError 

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

179 """ 

180  

181 # This is made public for testing purposes only 

182 cdef public intptr_t _handle 

183  

184 def __init__( 

185 self, 

186 *, 

187 index: int | None = None, 

188 uuid: bytes | str | None = None, 

189 pci_bus_id: bytes | str | None = None, 

190 ) -> None: 

191 args = [index, uuid, pci_bus_id] 1sbtucgvJpawxKVhQyLkqzoAMlBNrOCDEfndiFTUGeHPjRSm

192 cdef int arg_count = sum(arg is not None for arg in args) 1sbtucgvJpawxKVhQyLkqzoAMlBNrOCDEfndiFTUGeHPjRSm

193  

194 if arg_count > 1: 1sbtucgvJpawxKVhQyLkqzoAMlBNrOCDEfndiFTUGeHPjRSm

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

196 if arg_count == 0: 1sbtucgvJpawxKVhQyLkqzoAMlBNrOCDEfndiFTUGeHPjRSm

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

198  

199 initialize() 1sbtucgvJpawxKhQyLkqzoAMlBNrOCDEfndiFTUGeHPjRSm

200  

201 if index is not None: 1sbtucgvJpawxKhQyLkqzoAMlBNrOCDEfndiFTUGeHPjRSm

202 self._handle = nvml.device_get_handle_by_index_v2(index) 1sbtucgpawxKhyLzoAlBNrOCDEfndiFTUGeHP

203 elif uuid is not None: 1vJQkqMjRSm

204 if isinstance(uuid, bytes): 1vJQkqMjRSm

205 uuid = uuid.decode("ascii") 

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

207 elif pci_bus_id is not None: 1kj

208 if isinstance(pci_bus_id, bytes): 1kj

209 pci_bus_id = pci_bus_id.decode("ascii") 

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

211  

212 ######################################################################### 

213 # BASIC PROPERTIES 

214  

215 @property 

216 def index(self) -> int: 

217 """ 

218 The NVML index of this device. 

219  

220 Valid indices are derived from the count returned by 

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

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

223 1. 

224  

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

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

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

228  

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

230 device index. 

231 """ 

232 return nvml.device_get_index(self._handle) 1sbtucgvJwxhykqzMlIBOCDEfndFGeHj

233  

234 @property 

235 def uuid(self) -> str: 

236 """ 

237 Retrieves the globally unique immutable UUID associated with this 

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

239 board serial identifier. 

240  

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

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

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

244 property. 

245 """ 

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

247  

248 @property 

249 def uuid_without_prefix(self) -> str: 

250 """ 

251 Retrieves the globally unique immutable UUID associated with this 

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

253 board serial identifier. 

254  

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

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

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

258 property. 

259 """ 

260 uuid = self.uuid 1oeWXYZm

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

262 return uuid[4:] 1oeWXYm

263 return uuid 1Z

264  

265 @property 

266 def pci_bus_id(self) -> str: 

267 """ 

268 Retrieves the PCI bus ID of this device. 

269 """ 

270 return self.pci_info.bus_id 1j

271  

272 @property 

273 def numa_node_id(self) -> int: 

274 """ 

275 The NUMA node of the given GPU device. 

276  

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

278 """ 

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

280  

281 @property 

282 def arch(self) -> DeviceArch: 

283 """ 

284 :obj:`~DeviceArch` device architecture. 

285  

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

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

288 "AMPERE"``. 

289 """ 

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

291 try: 1p

292 return DeviceArch(arch) 1p

293 except ValueError: 

294 return DeviceArch.UNKNOWN 

295  

296 @property 

297 def name(self) -> str: 

298 """ 

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

300 """ 

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

302  

303 @property 

304 def brand(self) -> str: 

305 """ 

306 The brand of the device. 

307  

308 Returns "Unknown" if the brand is unknown. 

309 """ 

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

311  

312 @property 

313 def serial(self) -> str: 

314 """ 

315 Retrieves the globally unique board serial number associated with this 

316 device's board. 

317  

318 For all products with an InfoROM. 

319 """ 

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

321  

322 @property 

323 def module_id(self) -> int: 

324 """ 

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

326  

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

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

329 be 0. 

330 """ 

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

332  

333 @property 

334 def minor_number(self) -> int: 

335 """ 

336 The minor number of this device. 

337  

338 For Linux only. 

339  

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

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

342 """ 

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

344  

345 @property 

346 def is_c2c_enabled(self) -> bool: 

347 """ 

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

349 """ 

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

351  

352 @property 

353 def is_persistence_mode_enabled(self) -> bool: 

354 """ 

355 Whether persistence mode is enabled for this device. 

356  

357 For Linux only. 

358 """ 

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

360  

361 @is_persistence_mode_enabled.setter 

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

363 nvml.device_set_persistence_mode( 1n

364 self._handle, 1n

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

366 ) 

367  

368 @property 

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

370 """ 

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

372  

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

374 """ 

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

376  

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

378 """ 

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

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

381 NVIDIA Management Library (NVML) access). 

382  

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

384  

385 Returns 

386 ------- 

387 cuda.core.Device 

388 The corresponding CUDA device. 

389  

390 Raises 

391 ------ 

392 RuntimeError 

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

394  

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

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

397 though it can be enumerated from NVML. 

398 """ 

399 from cuda.core import Device as CudaDevice 1e

400  

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

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

403  

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

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

406 return cuda_device 1e

407  

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

409  

410 @classmethod 

411 def get_device_count(cls) -> int: 

412 """ 

413 Get the number of available devices. 

414  

415 Returns 

416 ------- 

417 int 

418 The number of available devices. 

419 """ 

420 initialize() 1567

421  

422 return nvml.device_get_count_v2() 1567

423  

424 @classmethod 

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

426 """ 

427 Query the available device instances. 

428  

429 Returns 

430 ------- 

431 Iterator over :obj:`~Device` 

432 An iterator over available devices. 

433 """ 

434 initialize() 1sbtucgpwxKhyLzoAlBNrOCDEfndiFGeHP

435  

436 for device_id in range(nvml.device_get_count_v2()): 1sbtucgpwxKhyLzoAlBNrOCDEfndiFGeHP

437 yield cls(index=device_id) 1sbtucgpwxKhyLzoAlBNrOCDEfndiFGeHP

438  

439 ######################################################################### 

440 # ADDRESSING MODE 

441  

442 @property 

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

444 """ 

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

446 """ 

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

448  

449 ######################################################################### 

450 # MIG (MULTI-INSTANCE GPU) DEVICES 

451  

452 @property 

453 def mig(self) -> MigInfo: 

454 """ 

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

456  

457 For Ampere™ or newer fully supported devices. 

458 """ 

459 return MigInfo(self) 1vC

460  

461 ######################################################################### 

462 # AFFINITY 

463  

464 @classmethod 

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

466 """ 

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

468  

469 Supported on Linux only. 

470  

471 Parameters 

472 ---------- 

473 cpu_index: int 

474 The CPU index. 

475  

476 Returns 

477 ------- 

478 Iterator of :obj:`~Device` 

479 An iterator over available devices. 

480 """ 

481 cdef Device device 

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

483 device = Device.__new__(Device) 1I

484 device._handle = handle 1I

485 yield device 1I

486  

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

488 """ 

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

490 memory affinity for the device. 

491  

492 For Kepler™ or newer fully supported devices. 

493  

494 Supported on Linux only. 

495  

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

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

498 ancestor of the device. 

499  

500 Parameters 

501 ---------- 

502 scope: AffinityScope | str, optional 

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

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

505  

506 Returns 

507 ------- 

508 list[int] 

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

510 affinity for the device. 

511 """ 

512 try: 1ba

513 scope = _AFFINITY_SCOPE_MAPPING[scope] 1ba

514 except KeyError: 1a

515 raise ValueError( 1a

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

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

518 ) from None 1a

519 return _unpack_bitmask( 1b

520 nvml.device_get_memory_affinity( 1b

521 self._handle, 1b

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

523 scope, 1b

524 ) 

525 ) 

526  

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

528 """ 

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

530 CPU affinity for the device. 

531  

532 For Kepler™ or newer fully supported devices. 

533  

534 Supported on Linux only. 

535  

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

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

538 ancestor of the device. 

539  

540 Parameters 

541 ---------- 

542 scope: AffinityScope | str, optional 

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

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

545  

546 Returns 

547 ------- 

548 list[int] 

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

550 affinity for the device. 

551 """ 

552 try: 1bahI

553 scope = _AFFINITY_SCOPE_MAPPING[scope] 1bahI

554 except KeyError: 1a

555 raise ValueError( 1a

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

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

558 ) from None 1a

559 return _unpack_bitmask( 1bhI

560 nvml.device_get_cpu_affinity_within_scope( 1bhI

561 self._handle, 1bhI

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

563 scope, 1bhI

564 ) 

565 ) 

566  

567 def set_cpu_affinity(self) -> None: 

568 """ 

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

570  

571 For Kepler™ or newer fully supported devices. 

572  

573 Supported on Linux only. 

574 """ 

575 nvml.device_set_cpu_affinity(self._handle) 

576  

577 def clear_cpu_affinity(self) -> None: 

578 """ 

579 Clear all affinity bindings for the calling thread. 

580  

581 For Kepler™ or newer fully supported devices. 

582  

583 Supported on Linux only. 

584 """ 

585 nvml.device_clear_cpu_affinity(self._handle) 

586  

587 ######################################################################### 

588 # CLOCK 

589 # See external class definitions in _clock.pxi 

590  

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

592 """ 

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

594 """ 

595 return ClockInfo(self._handle, clock_type) 1g

596  

597 @property 

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

599 """ 

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

601  

602 For Kepler™ or newer fully supported devices. 

603  

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

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

606 limits allow. 

607  

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

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

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

611  

612 Returns 

613 ------- 

614 bool 

615 The current state of Auto Boosted clocks 

616 bool 

617 The default Auto Boosted clocks behavior 

618  

619 """ 

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

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

622  

623 @property 

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

625 """ 

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

627  

628 For all fully supported products. 

629 """ 

630 cdef uint64_t[1] reasons 

631 reasons[0] = nvml.device_get_current_clocks_event_reasons(self._handle) 1c

632 output_reasons = [] 1c

633 for reason in _unpack_bitmask(reasons): 1c

634 try: 1c

635 output_reason = _CLOCKS_EVENT_REASONS_MAPPING[1 << reason] 1c

636 except KeyError: 

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

638 output_reasons.append(output_reason) 1c

639 return output_reasons 1c

640  

641 @property 

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

643 """ 

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

645 :meth:`get_current_clock_event_reasons`. 

646  

647 For all fully supported products. 

648  

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

650 """ 

651 cdef uint64_t[1] reasons 

652 reasons[0] = nvml.device_get_supported_clocks_event_reasons(self._handle) 1c

653 output_reasons = [] 1c

654 for reason in _unpack_bitmask(reasons): 1c

655 try: 1c

656 output_reason = _CLOCKS_EVENT_REASONS_MAPPING[1 << reason] 1c

657 except KeyError: 

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

659 output_reasons.append(output_reason) 1c

660 return output_reasons 1c

661  

662 ########################################################################## 

663 # COOLER 

664 # See external class definitions in _cooler.pxi 

665  

666 @property 

667 def cooler(self) -> CoolerInfo: 

668 """ 

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

670 """ 

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

672  

673 ########################################################################## 

674 # DEVICE ATTRIBUTES 

675 # See external class definitions in _device_attributes.pxi 

676  

677 @property 

678 def attributes(self) -> DeviceAttributes: 

679 """ 

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

681  

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

683 systems. 

684 """ 

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

686  

687 ######################################################################### 

688 # DISPLAY 

689  

690 @property 

691 def is_display_connected(self) -> bool: 

692 """ 

693 The display mode for this device. 

694  

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

696 any of the device's connectors. 

697 """ 

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

699  

700 @property 

701 def is_display_active(self) -> bool: 

702 """ 

703 The display active status for this device. 

704  

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

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

707 the screen. 

708  

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

710 """ 

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

712  

713 ########################################################################## 

714 # EVENTS 

715 # See external class definitions in _event.pxi 

716  

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

718 """ 

719 Starts recording events on this device. 

720  

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

722  

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

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

725 available only on Power Management enabled devices (see 

726 :meth:`Device.get_power_management_mode`). 

727  

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

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

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

731  

732 Examples 

733 -------- 

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

735 >>> events = device.register_events([ 

736 ... EventType.XID_CRITICAL_ERROR, 

737 ... ]) 

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

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

740  

741 Parameters 

742 ---------- 

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

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

745  

746 Returns 

747 ------- 

748 :obj:`~_device.DeviceEvents` 

749 An object representing the registered events. Call 

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

751  

752 Raises 

753 ------ 

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

755 None of the requested event types are registered. 

756 """ 

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

758  

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

760 """ 

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

762  

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

764 empty list on Windows). 

765  

766 Returns 

767 ------- 

768 list[EventType] 

769 The list of supported event types. 

770 """ 

771 cdef uint64_t[1] bitmask 

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

773 events = [] 1i

774 for ev in _unpack_bitmask(bitmask): 1i

775 try: 1i

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

777 except KeyError: 

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

779 events.append(ev_enum) 1i

780 return events 1i

781  

782 ########################################################################## 

783 # FAN 

784 # See external class definitions in _fan.pxi 

785  

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

787 """ 

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

789 """ 

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

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

792 return FanInfo(self._handle, fan) 

793  

794 @property 

795 def num_fans(self) -> int: 

796 """ 

797 The number of fans on the device. 

798 """ 

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

800  

801 ########################################################################## 

802 # FIELD VALUES 

803 # See external class definitions in _field_values.pxi 

804  

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

806 """ 

807 Get multiple field values from the device. 

808  

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

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

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

812  

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

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

815  

816 Parameters 

817 ---------- 

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

819 List of field IDs to query. 

820  

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

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

823  

824 Returns 

825 ------- 

826 :obj:`~_device.FieldValues` 

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

828 """ 

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

830 # so avoid that. 

831 if len(field_ids) == 0: 1lf30412

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

833  

834 return FieldValues(nvml.device_get_field_values(self._handle, field_ids)) 1lf30412

835  

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

837 """ 

838 Clear multiple field values from the device. 

839  

840 Parameters 

841 ---------- 

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

843 List of field IDs to clear. 

844  

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

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

847 """ 

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

849 # so avoid that. 

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

851 return 

852  

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

854  

855 ########################################################################## 

856 # INFOROM 

857 # See external class definitions in _inforom.pxi 

858  

859 @property 

860 def inforom(self) -> InforomInfo: 

861 """ 

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

863  

864 For all products with an InfoROM. 

865 """ 

866 return InforomInfo(self) 1B

867  

868 ########################################################################## 

869 # MEMORY 

870 # See external class definitions in _memory.pxi 

871  

872 @property 

873 def bar1_memory_info(self) -> BAR1MemoryInfo: 

874 """ 

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

876  

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

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

879 bus). 

880 """ 

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

882  

883 @property 

884 def memory_info(self) -> MemoryInfo: 

885 """ 

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

887 """ 

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

889  

890 ########################################################################## 

891 # NVLINK 

892 # See external class definitions in _nvlink.pxi 

893  

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

895 """ 

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

897  

898 For devices with NVLink support. 

899  

900 .. version-changed:: 1.1.0 

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

902 """ 

903 link_count = self.get_nvlink_count() 1f

904 if link < 0 or link >= link_count: 1f

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

906 return NvlinkInfo(self, link) 1f

907  

908 def get_nvlink_count(self) -> int: 

909 """ 

910 Get the number of NVLink links on this device. 

911  

912 For devices with NVLink support. 

913  

914 .. version-added:: 1.1.0 

915 """ 

916 return self.get_field_values([FieldId.DEV_NVLINK_LINK_COUNT])[0].value 1f30412

917  

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

919 """ 

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

921  

922 For devices with NVLink support. 

923  

924 .. version-added:: 1.1.0 

925 """ 

926 for link in range(self.get_nvlink_count()): 1f012

927 yield self.get_nvlink(link) 1f

928  

929 ########################################################################## 

930 # PCI INFO 

931 # See external class definitions in _pci_info.pxi 

932  

933 @property 

934 def pci_info(self) -> PciInfo: 

935 """ 

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

937  

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

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

940 """ 

941 try: 1kqejm

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

943 except nvml.InvalidArgumentError: 

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

945 else: 

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

947  

948 ########################################################################## 

949 # PERFORMANCE 

950 # See external class definitions in _performance.pxi 

951  

952 @property 

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

954 """ 

955 The current performance state of the device. 

956  

957 For Fermi™ or newer fully supported devices. 

958  

959 Returns 

960 ------- 

961 int | None 

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

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

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

965 """ 

966 return _pstate_to_int(nvml.device_get_performance_state(self._handle)) 1gd

967  

968 @property 

969 def dynamic_pstates_info(self) -> GpuDynamicPstatesInfo: 

970 """ 

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

972 """ 

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

974  

975 @property 

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

977 """ 

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

979  

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

981 the device. 

982  

983 Return 

984 ------ 

985 list[int] 

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

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

988 are lower performance. 

989 """ 

990 # From nvml.h: 

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

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

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

994 # NVML_PSTATE_UNKNOWN. 

995  

996 pstates = [] 1d

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

998 pstate_value = _pstate_to_int(pstate) 1d

999 if pstate_value is not None: 1d

1000 pstates.append(pstate_value) 1d

1001 return pstates 1d

1002  

1003 ########################################################################## 

1004 # PROCESS 

1005 # See external class definitions in _process.pxi 

1006  

1007 @property 

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

1009 """ 

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

1011  

1012 For Fermi™ or newer fully supported devices. 

1013  

1014 This function returns information only about compute running processes 

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

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

1017 function. 

1018  

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

1020 number of elements might change in time. 

1021  

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

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

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

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

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

1027 """ 

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

1029  

1030 ########################################################################## 

1031 # REPAIR STATUS 

1032 # See external class definitions in _repair_status.pxi 

1033  

1034 @property 

1035 def repair_status(self) -> RepairStatus: 

1036 """ 

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

1038  

1039 For Ampere™ or newer fully supported devices. 

1040 """ 

1041 return RepairStatus(self._handle) 1F

1042  

1043 ########################################################################## 

1044 # TEMPERATURE 

1045 # See external class definitions in _temperature.pxi 

1046  

1047 @property 

1048 def temperature(self) -> Temperature: 

1049 """ 

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

1051 """ 

1052 return Temperature(self._handle) 1TUG

1053  

1054 ####################################################################### 

1055 # TOPOLOGY 

1056  

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

1058 """ 

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

1060  

1061 Supported on Linux only. 

1062  

1063 Parameters 

1064 ---------- 

1065 level: :class:`GpuTopologyLevel` 

1066 The topology level. 

1067  

1068 Returns 

1069 ------- 

1070 Iterable of :class:`Device` 

1071 The nearest devices at the given topology level. 

1072 """ 

1073 cdef Device device 

1074 try: 1ar

1075 level = _GPU_TOPOLOGY_LEVEL_MAPPING[level] 1ar

1076 except KeyError: 1a

1077 raise ValueError( 1a

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

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

1080 ) from None 1a

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

1082 device = Device.__new__(Device) 

1083 device._handle = handle 

1084 yield device 

1085  

1086 ####################################################################### 

1087 # UTILIZATION 

1088  

1089 @property 

1090 def utilization(self) -> Utilization: 

1091 """ 

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

1093 subsystems. 

1094  

1095 For Fermi™ or newer fully supported devices. 

1096  

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

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

1099 Scrubbing mechanism that is performed during driver initialization. 

1100  

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

1102 currently supported. 

1103  

1104 Returns 

1105 ------- 

1106 Utilization 

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

1108 """ 

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

1110  

1111  

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

1113 """ 

1114 Retrieve the common ancestor for two devices. 

1115  

1116 For Linux only. 

1117  

1118 Parameters 

1119 ---------- 

1120 device1: :class:`Device` 

1121 The first device. 

1122 device2: :class:`Device` 

1123 The second device. 

1124  

1125 Returns 

1126 ------- 

1127 :class:`GpuTopologyLevel` 

1128 The common ancestor level of the two devices. 

1129 """ 

1130 return _GPU_TOPOLOGY_LEVEL_INV_MAPPING[ 

1131 nvml.device_get_topology_common_ancestor( 

1132 device1._handle, 

1133 device2._handle, 

1134 ) 

1135 ] 

1136  

1137  

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

1139 """ 

1140 Retrieve the P2P status between two devices. 

1141  

1142 Parameters 

1143 ---------- 

1144 device1: :class:`Device` 

1145 The first device. 

1146 device2: :class:`Device` 

1147 The second device. 

1148 index: :class:`GpuP2PCapsIndex` | str 

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

1150  

1151 Returns 

1152 ------- 

1153 :class:`GpuP2PStatus` 

1154 The P2P status between the two devices. 

1155 """ 

1156 try: 1a

1157 index_enum = _GPU_P2P_CAPS_INDEX_MAPPING[index] 1a

1158 except KeyError: 1a

1159 raise ValueError( 1a

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

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

1162 ) from None 1a

1163 return _GPU_P2P_STATUS_MAPPING.get( 

1164 nvml.device_get_p2p_status( 

1165 device1._handle, 

1166 device2._handle, 

1167 index_enum, 

1168 ), 

1169 GpuP2PStatus.UNKNOWN 

1170 ) 

1171  

1172  

1173__all__ = [ 

1174 "Device", 

1175 "get_p2p_status", 

1176 "get_topology_common_ancestor", 

1177 "NvlinkInfo", 

1178]