@@ -15,6 +15,9 @@ from ._nvml_context cimport initialize
1515include " _device_utils.pxi"
1616
1717
18+ FieldId = nvml.FieldId
19+
20+
1821class DeviceArchitecture :
1922 """
2023 Device architecture enumeration.
@@ -171,6 +174,141 @@ cdef class PciInfo:
171174 return self._pci_info.pci_device_id >> 16
172175
173176
177+ cdef class FieldValue:
178+ """
179+ Represents the data from a single field value.
180+
181+ Use :meth:`Device.get_field_values` to get multiple field values at once.
182+ """
183+ cdef object _field_value
184+
185+ def __init__(self , field_value: nvml.FieldValue ):
186+ assert len (field_value) == 1
187+ self ._field_value = field_value
188+
189+ @property
190+ def field_id (self ) -> FieldId:
191+ """
192+ The field ID.
193+ """
194+ return FieldId(self._field_value.field_id )
195+
196+ @property
197+ def scope_id(self ) -> int:
198+ """
199+ The scope ID.
200+ """
201+ # Explicit int() cast required because this is a Numpy type
202+ return int(self._field_value.scope_id )
203+
204+ @property
205+ def timestamp(self ) -> int:
206+ """
207+ The CPU timestamp (in microseconds since 1970) at which the value was
208+ sampled.
209+ """
210+ # Explicit int() cast required because this is a Numpy type
211+ return int(self._field_value.timestamp )
212+
213+ @property
214+ def latency_usec(self ) -> int:
215+ """
216+ How long this field value took to update (in usec ) within NVML. This may
217+ be averaged across several fields that are serviced by the same driver
218+ call.
219+ """
220+ # Explicit int() cast required because this is a Numpy type
221+ return int(self._field_value.latency_usec )
222+
223+ @property
224+ def value(self ) -> int | float:
225+ """
226+ The field value.
227+
228+ Raises
229+ ------
230+ :class:`cuda.core.system.NvmlError`
231+ If there was an error retrieving the field value.
232+ """
233+ nvml.check_status(self._field_value.nvml_return )
234+
235+ cdef int value_type = self ._field_value.value_type
236+ value = self ._field_value.value
237+
238+ ValueType = nvml.ValueType
239+
240+ if value_type == ValueType.DOUBLE:
241+ return float(value.d_val[0])
242+ elif value_type == ValueType.UNSIGNED_INT:
243+ return int(value.ui_val[0])
244+ elif value_type == ValueType.UNSIGNED_LONG:
245+ return int(value.ul_val[0])
246+ elif value_type == ValueType.UNSIGNED_LONG_LONG:
247+ return int(value.ull_val[0])
248+ elif value_type == ValueType.SIGNED_LONG_LONG:
249+ return int(value.ll_val[0])
250+ elif value_type == ValueType.SIGNED_INT:
251+ return int(value.si_val[0])
252+ elif value_type == ValueType.UNSIGNED_SHORT:
253+ return int(value.us_val[0])
254+ else:
255+ raise AssertionError("Unexpected value type")
256+
257+
258+ cdef class FieldValues:
259+ """
260+ Container of multiple field values.
261+ """
262+ cdef object _field_values
263+
264+ def __init__(self , field_values: nvml.FieldValue ):
265+ self ._field_values = field_values
266+
267+ def __getitem__ (self , idx: int ) -> FieldValue:
268+ return FieldValue(self._field_values[idx])
269+
270+ def __len__(self ) -> int:
271+ return len(self._field_values )
272+
273+ def validate(self ) -> None:
274+ """
275+ Validate that there are no issues in any of the contained field values.
276+
277+ Raises an exception for the first issue found , if any.
278+
279+ Raises
280+ ------
281+ :class:`cuda.core.system.NvmlError`
282+ If any of the contained field values has an associated exception.
283+ """
284+ # TODO: This is a classic use case for an `ExceptionGroup`, but those
285+ # are only available in Python 3.11+.
286+ return_values = self ._field_values.nvml_return
287+ if len(self._field_values ) == 1:
288+ return_values = [return_values]
289+ for return_value in return_values:
290+ nvml.check_status(return_value )
291+
292+ def get_all_values(self ) -> list[int | float]:
293+ """
294+ Get all field values as a list.
295+
296+ This will validate each of the values and include just the core value in
297+ the list.
298+
299+ Returns
300+ -------
301+ list[int | float]
302+ List of all field values.
303+
304+ Raises
305+ ------
306+ :class:`cuda.core.system.NvmlError`
307+ If any of the contained field values has an associated exception.
308+ """
309+ return [x.value for x in self]
310+
311+
174312cdef class Device:
175313 """
176314 Representation of a device.
@@ -313,11 +451,54 @@ cdef class Device:
313451 """
314452 return nvml.device_get_uuid(self._handle )
315453
454+ def get_field_values(self , field_ids: list[int | tuple[int , int]]) -> FieldValues:
455+ """
456+ Get multiple field values from the device.
457+
458+ Each value specified can raise its own exception. That exception will
459+ be raised when attempting to access the corresponding ``value`` from the
460+ returned :class:`FieldValues` container.
461+
462+ To confirm that there are no exceptions in the entire container , call
463+ :meth:`FieldValues.validate`.
464+
465+ Parameters
466+ ----------
467+ field_ids: list of int or tuple of (int , int )
468+ List of field IDs to query.
469+
470+ Each item may be either a single value from the :class:`FieldId`
471+ enum , or a pair of (:class:`FieldId`, scope ID ).
472+
473+ Returns
474+ -------
475+ :class:`FieldValues`
476+ Container of field values corresponding to the requested field IDs.
477+ """
478+ return FieldValues(nvml.device_get_field_values(self._handle , field_ids ))
479+
480+ def clear_field_values(self , field_ids: list[int | tuple[int , int]]) -> None:
481+ """
482+ Clear multiple field values from the device.
483+
484+ Parameters
485+ ----------
486+ field_ids: list of int or tuple of (int , int )
487+ List of field IDs to clear.
488+
489+ Each item may be either a single value from the :class:`FieldId`
490+ enum , or a pair of (:class:`FieldId`, scope ID ).
491+ """
492+ nvml.device_clear_field_values(self._handle , field_ids )
493+
316494
317495__all__ = [
318496 " BAR1MemoryInfo" ,
319497 " Device" ,
320498 " DeviceArchitecture" ,
499+ " FieldId" ,
500+ " FieldValue" ,
501+ " FieldValues" ,
321502 " MemoryInfo" ,
322503 " PciInfo" ,
323504]
0 commit comments