Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifecycle problems with device_by_index #20

Closed
thezealousfool opened this issue Nov 27, 2021 · 3 comments
Closed

Lifecycle problems with device_by_index #20

thezealousfool opened this issue Nov 27, 2021 · 3 comments
Labels

Comments

@thezealousfool
Copy link

This is what I am trying

pub struct GpuInfo<'a> {
    _instance: NVML,
    _devices: Option<Vec<Device<'a>>>,
    count: u32,
}

impl<'a> GpuInfo<'a> {
    pub fn new() -> Result<GpuInfo<'static>, NvmlError> {
        let _instance: NVML = NVML::init()?;
        let count = _instance.device_count()?;
        Ok(GpuInfo {
            _instance,
            _devices: None,
            count,
        })
    }

    pub fn get_devices(&mut self) {
        self._devices = Some(
            (0..self.count)
                .filter_map(|i| self._instance.device_by_index(i).ok())
                .collect(),
        );
    }
}

But it does not work. It gives me lifecycle errors. Specifically:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/gpu.rs:49:48
   |
49 |                 .filter_map(|i| self._instance.device_by_index(i).ok())
   |                                                ^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 46:5...
  --> src/gpu.rs:46:5
   |
46 |     pub fn get_devices(&mut self) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/gpu.rs:49:33
   |
49 |                 .filter_map(|i| self._instance.device_by_index(i).ok())
   |                                 ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 28:6...
  --> src/gpu.rs:28:6
   |
28 | impl<'a> GpuInfo<'a> {
   |      ^^
note: ...so that the expression is assignable
  --> src/gpu.rs:47:25
   |
47 |           self._devices = Some(
   |  _________________________^
48 | |             (0..self.count)
49 | |                 .filter_map(|i| self._instance.device_by_index(i).ok())
50 | |                 .collect(),
51 | |         );
   | |_________^
   = note: expected `Option<Vec<nvml_wrapper::Device<'a>>>`
              found `Option<Vec<nvml_wrapper::Device<'_>>>`

I am not sure if this is related to #2 or not. Any suggestions on how to fix this?

@Cldfire
Copy link
Owner

Cldfire commented Dec 1, 2021

Yeah, so due to the usage of lifetimes to track the relationships between the NVML instance and the Device structs obtained from it, it's not possible to store both the NVML instance and Device structs within the same object. (It creates a self-referential lifetime situation which isn't fun to deal with in Rust).

The best solution here is to initialize a global, static NVML instance and store it in a once_cell at program startup. You can then store devices obtained from that global instance within a struct. Here's an example:

use nvml_wrapper::{error::NvmlError, Device, NVML};
use once_cell::sync::Lazy;

static NVML_INSTANCE: Lazy<NVML> = Lazy::new(|| NVML::init().unwrap());

#[derive(Debug)]
struct NvmlDevices<'a> {
    devices: Vec<Device<'a>>,
}

impl<'a> NvmlDevices<'a> {
    pub fn new() -> Result<Self, NvmlError> {
        Ok(NvmlDevices {
            devices: (0..NVML_INSTANCE.device_count()?)
                .map(|i| NVML_INSTANCE.device_by_index(i))
                .collect::<Result<_, _>>()?,
        })
    }
}

fn main() {
    let devices = NvmlDevices::new();
    dbg!(&devices);
}

(I'm traveling and only have access to a mac right now, so I can't run that at the moment, but it compiles successfully for me.)

In the past I've thought about moving away from lifetimes and instead using Arcs to model the lifetime relationships (which would be easier to deal with and make your code example possible to write). Since this library doesn't provide performance-critical code, I think that might be the better path to go down. If I find time and interest I'll play around with implementing that in this library sometime in the future.

@thezealousfool
Copy link
Author

@Cldfire I checked, it works.

I think the Arc implementation might be the way to go if the Device instances have to have a reference to the NVML instance.

Thank you.

@Cldfire
Copy link
Owner

Cldfire commented Dec 3, 2021

Awesome! Glad to hear that worked out for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants