diff --git a/CHANGELOG.md b/CHANGELOG.md index 3361cc8..ed283ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ This file describes the changes / additions / fixes between wrapper releases, tr ## [Unreleased] +### Added + +* `ScopeId` +* `FieldValueRequest` + +### Changed + +* `Device` + * Methods + * `field_values_for()` +* `FieldValueSample` + ## [0.10.0] (released 2024-02-10) Updates for NVML 12.2. diff --git a/nvml-wrapper/src/device.rs b/nvml-wrapper/src/device.rs index 2e46a24..5175c4f 100644 --- a/nvml-wrapper/src/device.rs +++ b/nvml-wrapper/src/device.rs @@ -33,6 +33,7 @@ use std::{ convert::TryFrom, ffi::CStr, mem, + ops::Deref, os::raw::{c_int, c_uint, c_ulonglong}, ptr, }; @@ -2652,9 +2653,9 @@ impl<'nvml> Device<'nvml> { } /** - Get values for the given slice of `FieldId`s. + Get values for the given `FieldValueRequest`s. - NVIDIA's docs say that if any of the `FieldId`s are populated by the same driver + NVIDIA's docs say that if any of the `FieldValueRequest`s are populated by the same driver call, the samples for those IDs will be populated by a single call instead of a call per ID. It would appear, then, that this is essentially a "batch-request" API path for better performance. @@ -2662,7 +2663,7 @@ impl<'nvml> Device<'nvml> { There are too many field ID constants defined in the header to reasonably wrap them with an enum in this crate. Instead, I've re-exported the defined ID constants at `nvml_wrapper::sys_exports::field_id::*`; stick those - constants in `FieldId`s for use with this function. + constants in `FieldValueRequest`s for use with this function. # Errors @@ -2676,30 +2677,33 @@ impl<'nvml> Device<'nvml> { # Device Support - Device support varies per `FieldId` that you pass in. + Device support varies per `FieldValueRequest` that you pass in. */ // TODO: Example #[doc(alias = "nvmlDeviceGetFieldValues")] - pub fn field_values_for( + pub fn field_values_for( &self, - id_slice: &[FieldId], - ) -> Result>, NvmlError> { + ids: I, + ) -> Result>, NvmlError> + where + I: IntoIterator, + I::Item: Deref, + { let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetFieldValues.as_ref())?; unsafe { - let values_count = id_slice.len(); - let mut field_values: Vec = Vec::with_capacity(values_count); - - for id in id_slice.iter() { - let mut raw: nvmlFieldValue_t = mem::zeroed(); - raw.fieldId = id.0; - - field_values.push(raw); - } + let mut field_values: Vec = ids + .into_iter() + .map(|id| nvmlFieldValue_t { + fieldId: id.id.0, + scopeId: id.scope_id.clone().unwrap_or(ScopeId(0)).0, + ..mem::zeroed() + }) + .collect(); nvml_try(sym( self.device, - values_count as i32, + field_values.len() as i32, field_values.as_mut_ptr(), ))?; @@ -5070,7 +5074,7 @@ mod test { use crate::enum_wrappers::device::*; use crate::enums::device::GpuLockedClocksSetting; use crate::error::*; - use crate::structs::device::FieldId; + use crate::structs::device::{FieldId, FieldValueRequest}; use crate::sys_exports::field_id::*; use crate::test_utils::*; @@ -5558,44 +5562,44 @@ mod test { let nvml = nvml(); test_with_device(3, &nvml, |device| { device.field_values_for(&[ - FieldId(NVML_FI_DEV_ECC_CURRENT), - FieldId(NVML_FI_DEV_ECC_PENDING), - FieldId(NVML_FI_DEV_ECC_SBE_VOL_TOTAL), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_TOTAL), - FieldId(NVML_FI_DEV_ECC_SBE_AGG_TOTAL), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_TOTAL), - FieldId(NVML_FI_DEV_ECC_SBE_VOL_L1), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_L1), - FieldId(NVML_FI_DEV_ECC_SBE_VOL_L2), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_L2), - FieldId(NVML_FI_DEV_ECC_SBE_VOL_DEV), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_DEV), - FieldId(NVML_FI_DEV_ECC_SBE_VOL_REG), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_REG), - FieldId(NVML_FI_DEV_ECC_SBE_VOL_TEX), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_TEX), - FieldId(NVML_FI_DEV_ECC_DBE_VOL_CBU), - FieldId(NVML_FI_DEV_ECC_SBE_AGG_L1), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_L1), - FieldId(NVML_FI_DEV_ECC_SBE_AGG_L2), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_L2), - FieldId(NVML_FI_DEV_ECC_SBE_AGG_DEV), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_DEV), - FieldId(NVML_FI_DEV_ECC_SBE_AGG_REG), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_REG), - FieldId(NVML_FI_DEV_ECC_SBE_AGG_TEX), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_TEX), - FieldId(NVML_FI_DEV_ECC_DBE_AGG_CBU), - FieldId(NVML_FI_DEV_PERF_POLICY_POWER), - FieldId(NVML_FI_DEV_PERF_POLICY_THERMAL), - FieldId(NVML_FI_DEV_PERF_POLICY_SYNC_BOOST), - FieldId(NVML_FI_DEV_PERF_POLICY_BOARD_LIMIT), - FieldId(NVML_FI_DEV_PERF_POLICY_LOW_UTILIZATION), - FieldId(NVML_FI_DEV_PERF_POLICY_RELIABILITY), - FieldId(NVML_FI_DEV_PERF_POLICY_TOTAL_APP_CLOCKS), - FieldId(NVML_FI_DEV_PERF_POLICY_TOTAL_BASE_CLOCKS), - FieldId(NVML_FI_DEV_MEMORY_TEMP), - FieldId(NVML_FI_DEV_TOTAL_ENERGY_CONSUMPTION), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_CURRENT)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_PENDING)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_VOL_TOTAL)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_TOTAL)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_AGG_TOTAL)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_TOTAL)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_VOL_L1)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_L1)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_VOL_L2)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_L2)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_VOL_DEV)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_DEV)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_VOL_REG)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_REG)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_VOL_TEX)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_TEX)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_VOL_CBU)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_AGG_L1)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_L1)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_AGG_L2)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_L2)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_AGG_DEV)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_DEV)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_AGG_REG)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_REG)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_SBE_AGG_TEX)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_TEX)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_ECC_DBE_AGG_CBU)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_POWER)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_THERMAL)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_SYNC_BOOST)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_BOARD_LIMIT)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_LOW_UTILIZATION)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_RELIABILITY)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_TOTAL_APP_CLOCKS)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_PERF_POLICY_TOTAL_BASE_CLOCKS)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_MEMORY_TEMP)), + FieldValueRequest::id(FieldId(NVML_FI_DEV_TOTAL_ENERGY_CONSUMPTION)), ]) }) } diff --git a/nvml-wrapper/src/struct_wrappers/device.rs b/nvml-wrapper/src/struct_wrappers/device.rs index 213d16b..b4e8cc4 100644 --- a/nvml-wrapper/src/struct_wrappers/device.rs +++ b/nvml-wrapper/src/struct_wrappers/device.rs @@ -3,7 +3,7 @@ use crate::enum_wrappers::device::{BridgeChip, EncoderType, FbcSessionType, Samp use crate::enums::device::{FirmwareVersion, SampleValue, UsedGpuMemory}; use crate::error::{nvml_try, Bits, NvmlError}; use crate::ffi::bindings::*; -use crate::structs::device::FieldId; +use crate::structs::device::{FieldId, ScopeId}; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; use std::{ @@ -528,6 +528,8 @@ impl From for ProcessUtilizationSample { pub struct FieldValueSample { /// The field that this sample is for. pub field: FieldId, + /// The field scope that this sample is for. + pub scope: ScopeId, /// This sample's CPU timestamp in μs (Unix time). pub timestamp: i64, /** @@ -556,6 +558,7 @@ impl TryFrom for FieldValueSample { fn try_from(value: nvmlFieldValue_t) -> Result { Ok(Self { field: FieldId(value.fieldId), + scope: ScopeId(value.scopeId), timestamp: value.timestamp, latency: value.latencyUsec, value: match nvml_try(value.nvmlReturn) { diff --git a/nvml-wrapper/src/structs/device.rs b/nvml-wrapper/src/structs/device.rs index 2176607..0f8113b 100644 --- a/nvml-wrapper/src/structs/device.rs +++ b/nvml-wrapper/src/structs/device.rs @@ -101,3 +101,40 @@ pub struct RetiredPage { #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FieldId(pub u32); + +/// Can be used to specify an optional scope to a given `FieldId` +/// +/// Used in `FieldValue` and `Device.field_values_for()`. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ScopeId(pub u32); + +/// Specify a field ID and an optional scope ID for requesting data samples +/// from a device. +/// +/// Used in [`crate::struct_wrappers::device::FieldValueSample`] and +/// [`crate::device::Device::field_values_for()`]. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct FieldValueRequest { + /// Populate this newtype with the constants `nvml_wrapper::sys_exports::field_id::*`. + pub id: FieldId, + /// Optionally populate this with a `scopeId` appropriate for the associated [`FieldId`]. + /// + /// See NVIDIA's field ID constant docs (`NVML_FI_*`) to understand what scope + /// IDs may be valid for which field IDs. + pub scope_id: Option, +} + +impl FieldValueRequest { + pub fn id(id: FieldId) -> Self { + Self { id, scope_id: None } + } + + pub fn id_with_scope(id: FieldId, scope_id: ScopeId) -> Self { + Self { + id, + scope_id: Some(scope_id), + } + } +}