nvml-wrapper

repository·main·Indexed 41 days ago

https://github.com/rust-nvml/nvml-wrapper

A safe and ergonomic Rust wrapper for the NVIDIA Management Library (NVML) version 0.13.0, used for monitoring and managing NVIDIA GPU states. It provides dynamic runtime loading of the NVML library to allow applications to handle systems without NVIDIA hardware gracefully. Features include querying GPU hardware metrics, clock speeds, memory usage, compute processes, and managing fan speeds and ECC modes.

Tokens
24.4K
Snippets
94
Records
150
Agent score
52%

What's inside nvml-wrapper

  1. How Nvml::init works and best practices

    main

    The Nvml::init method performs the heavy lifting of loading the NVML library and all required function symbols from the system.

    Best Practices:

    • Call Nvml::init only once: Do not call it repeatedly, as the symbol loading process is expensive.
    • Reuse the instance: Store the resulting Nvml instance (for example, in a once_cell::sync::Lazy) and access it throughout your program's lifetime.
    • Handle runtime absence: Because it uses dynamic loading, you can use the error returned by Nvml::init to decide whether to enable or disable NVIDIA-specific features at runtime.
  2. Understand the runtime loading behavior of nvml-wrapper-sys

    main

    The nvml-wrapper-sys crate provides Rust bindings for the NVIDIA Management Library (NVML) using bindgen and libloading.

    Key behavior: These bindings are designed to load the NVML library at runtime. They are not suitable for static or dynamic linking at buildtime.

    Why runtime loading is used:

    • NVIDIA does not distribute static versions of NVML.
    • Buildtime linking requires the target system to have NVIDIA GPUs and valid drivers present during the build process.
    • Runtime loading allows applications to gracefully drop NVIDIA-specific features on systems that lack the necessary hardware.
  3. Quickstart with nvml-wrapper

    main

    To use nvml-wrapper, initialize the Nvml instance and then access specific GPU devices via their index. This allows you to query hardware metrics like brand, fan speed, power limits, and memory usage.

    Note that Nvml::init() loads the NVML library dynamically using libloading. If the library is missing or initialization fails, it will return an error, allowing your application to gracefully handle systems without NVIDIA hardware.

    use nvml_wrapper::Nvml;
    
    let nvml = Nvml::init()?;
    // Get the first `Device` (GPU) in the system
    let device = nvml.device_by_index(0)?;
    
    let brand = device.brand()?; // e.g., "GeForce"
    let fan_speed = device.fan_speed(0)?; // e.g., 17
    let power_limit = device.enforced_power_limit()?; // e.g., 275000
    let encoder_util = device.encoder_utilization()?; 
    let memory_info = device.memory_info()?; 
  4. Enable legacy NVML functions via Cargo features

    main

    By default, these bindings only include the newest versions of NVML functions (e.g., nvmlDeviceGetComputeRunningProcesses_v3). While newer NVML releases are backwards-compatible, older function versions may be required for compatibility with older NVML installs.

    To include older versions of functions in your build, enable the legacy-functions Cargo feature.

    [dependencies]
    nvml-wrapper-sys = {
        version = "0.13.0",
        features = ["legacy-functions"]
    }
  5. How EventSet works for monitoring NVML events

    main

    An EventSet is a handle used to monitor NVML events (such as PSTATE changes or XID errors). You obtain an EventSet by calling Nvml.create_event_set().

    Key Characteristics:

    • Thread Safety: Operations on an EventSet are not thread-safe; it does not implement Sync.
    • Lifetimes: An EventSet is tied to the lifetime of the Nvml instance that created it. You cannot use an EventSet after the parent Nvml instance has been dropped.
    • Device Support: Supports Fermi and newer fully supported devices.

    To use it, you typically register specific event types to the set (via a device handle) and then call .wait() to listen for them.

    let nvml = Nvml::init()?;
    let event_set = nvml.create_event_set()?;
    
    // ... register events and wait ...
    
    drop(nvml); 
    // event_set.wait(...) would now fail to compile due to lifetime constraints
  6. Shutdown NVML safely

    main

    The Nvml struct implements Drop, which automatically calls nvmlShutdown. However, this Drop implementation ignores any errors that might occur during shutdown.

    If you need to handle potential errors during the shutdown process, you should explicitly call the .shutdown() method instead of relying on the struct going out of scope.

  7. Understand the `Device` struct and its lifecycle

    main

    The Device struct represents a physical GPU on the system. It is designed with Rust lifetimes to ensure memory safety: a Device instance cannot outlive the Nvml instance from which it was created. This prevents common errors like attempting to query a device after the NVML library has been uninitialized.

    To use a Device, you should use the methods provided by the Nvml struct (e.g., device_by_index) rather than constructing it manually.

    Safety Note: The handle() method returns the raw nvmlDevice_t pointer for C interop, but it is unsafe because using it without proper care can lead to undefined behavior.

    use nvml_wrapper::Nvml;
    
    // Correct usage pattern
    let nvml = Nvml::init()?;
    let device = nvml.device_by_index(0)?;
    
    // The following would cause a compile error because `device` depends on `nvml`
    drop(nvml);
    // device.clock_info(Clock::Graphics)?; 
  8. Manage Multi-Instance GPU (MIG) modes

    main

    MIG allows a single GPU to be split into isolated instances.

    Check MIG status

    Use mig_mode() to get the current and pending modes, or mig_is_mig_device_handle() to check if the current handle is a MIG device.

    Configure MIG

    Use set_mig_mode(bool) to enable or disable MIG. Note that setting this may fail if the device is currently in use.

    • mig_device_by_index(u32): Get a MIG device handle from an index on a parent physical GPU.
    • mig_parent_device(): Get the parent physical device from a MIG device handle.
    • mig_device_count(): Get the maximum number of MIG devices available on the physical GPU.
    let mode = device.mig_mode()?;
    device.set_mig_mode(true)?;
    
    if device.mig_is_mig_device_handle() {
        let parent = device.mig_parent_device()?;
    }
  9. Enable legacy NVML functions

    main

    By default, nvml-wrapper only provides access to the newest versions of NVML functions (e.g., nvmlDeviceGetComputeRunningProcesses_v3).

    If your environment requires calling older versions of specific functions (e.g., _v1 or _v2) to maintain compatibility with older NVML installs, you must enable the legacy-functions Cargo feature.

  10. How the `Unit` struct works and its lifetime constraints

    main

    The Unit struct represents a hardware unit (typically used for S-class products). It is obtained via methods on the Nvml struct.

    Lifetime Safety: Unit instances are tied to the lifetime of the Nvml instance from which they were created. You cannot use a Unit after the parent Nvml instance has been dropped. Attempting to do so will result in a compilation error.

    Hardware Note: Many Unit methods are specific to S-class products. If your hardware does not support these features, methods may return NvmlError::NotSupported.

    use nvml_wrapper::Nvml;
    
    let nvml = Nvml::init()?;
    let unit = nvml.unit_by_index(0)?;
    
    // unit is valid here because nvml is still in scope
    let devices = unit.devices()?;
    
    drop(nvml);
    
    // This would cause a compilation error:
    // let devices = unit.devices()?;
  11. How GPM (GPU Performance Monitoring) works

    main

    GPM enables collecting fine-grained GPU performance metrics (such as SM occupancy, tensor utilization, and PCIe/NVLink bandwidth) on Hopper+ GPUs.

    Metrics are not single-point measurements; they are computed by taking two time-separated samples and comparing them using the gpm_metrics_get function.

    Key constraints:

    • Architecture: Supports Hopper and newer architectures only.
    • Thread Safety: Operations on a GpmSample are not thread-safe (it does not implement Sync).
    • Lifetimes: A GpmSample is tied to the lifetime of the Nvml instance from which it was obtained; it cannot be used after the Nvml instance is dropped.