mod memory;
use candle_core::Device;
use candle_core::utils::cuda_is_available;
use candle_core::utils::metal_is_available;
pub(crate) use memory::budget_bytes;
use crate::error::CanddingError;
use crate::error::Result;
use crate::registry::Backend;
pub fn auto() -> Result<Device> {
if metal_is_available() {
return metal(0);
}
if cuda_is_available() {
return cuda(0);
}
Ok(Device::Cpu)
}
pub fn cpu() -> Device {
Device::Cpu
}
pub fn metal(ordinal: usize) -> Result<Device> {
Device::new_metal(ordinal).map_err(|e| device_error(Backend::Metal, e))
}
pub fn cuda(ordinal: usize) -> Result<Device> {
Device::new_cuda(ordinal).map_err(|e| device_error(Backend::Cuda, e))
}
fn device_error(backend: Backend, error: candle_core::Error) -> CanddingError {
match error {
candle_core::Error::NotCompiledWithMetalSupport
| candle_core::Error::NotCompiledWithCudaSupport => {
CanddingError::BackendNotCompiled { backend }
}
error => CanddingError::Device {
backend,
reason: error.to_string(),
},
}
}
pub fn describe(device: &Device) -> &'static str {
match device {
Device::Cpu => "cpu",
Device::Metal(_) => "metal",
Device::Cuda(_) => "cuda",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auto_matches_compiled_features() {
let device = auto().unwrap();
let expected = if cfg!(feature = "metal") {
"metal"
} else if cfg!(feature = "cuda") {
"cuda"
} else {
"cpu"
};
assert_eq!(describe(&device), expected);
}
#[test]
fn cpu_is_cpu() {
assert_eq!(describe(&cpu()), "cpu");
}
#[test]
fn a_backend_left_out_of_the_build_is_refused_as_not_compiled() {
if !cfg!(feature = "metal") {
assert!(
matches!(
metal(0),
Err(CanddingError::BackendNotCompiled {
backend: Backend::Metal
})
),
"{:?}",
metal(0).err()
);
}
if !cfg!(feature = "cuda") {
assert!(
matches!(
cuda(0),
Err(CanddingError::BackendNotCompiled {
backend: Backend::Cuda
})
),
"{:?}",
cuda(0).err()
);
}
}
#[test]
fn any_other_device_failure_keeps_the_backend_and_candles_message() {
let error = device_error(
Backend::Cuda,
candle_core::Error::Msg("no device 3".to_string()),
);
assert!(
matches!(&error, CanddingError::Device { backend: Backend::Cuda, reason } if reason == "no device 3"),
"{error:?}"
);
let left_out = device_error(
Backend::Metal,
candle_core::Error::NotCompiledWithMetalSupport,
);
assert!(
matches!(
left_out,
CanddingError::BackendNotCompiled {
backend: Backend::Metal
}
),
"{left_out:?}"
);
}
}