use crate::error::Result;
use ndarray::ArrayD;
use std::sync::Arc;
pub mod cpu;
pub mod simd; pub mod fused;
#[cfg(feature = "cuda")]
pub mod cuda;
#[cfg(feature = "metal")]
pub mod metal;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DeviceType {
Cpu,
#[cfg(feature = "cuda")]
Cuda(usize), #[cfg(feature = "metal")]
Metal,
#[cfg(target_arch = "wasm32")]
Wasm,
}
#[derive(Clone)]
pub struct Device {
device_type: DeviceType,
#[allow(dead_code)]
backend: BackendImpl,
}
#[derive(Clone)]
#[allow(dead_code)]
enum BackendImpl {
Cpu(Arc<cpu::CpuBackend>),
#[cfg(feature = "cuda")]
Cuda(Arc<cuda::CudaBackend>),
#[cfg(feature = "metal-backend")]
Metal(Arc<metal::MetalBackend>),
#[cfg(target_arch = "wasm32")]
Wasm(Arc<wasm::WasmBackend>),
}
impl Device {
pub fn cpu() -> Self {
Self {
device_type: DeviceType::Cpu,
backend: BackendImpl::Cpu(Arc::new(cpu::CpuBackend::new())),
}
}
#[cfg(feature = "cuda")]
pub fn cuda(index: usize) -> Result<Self> {
Ok(Self {
device_type: DeviceType::Cuda(index),
backend: BackendImpl::Cuda(Arc::new(cuda::CudaBackend::new(index)?)),
})
}
#[cfg(feature = "metal-backend")]
pub fn metal() -> Result<Self> {
Ok(Self {
device_type: DeviceType::Metal,
backend: BackendImpl::Metal(Arc::new(metal::MetalBackend::new()?)),
})
}
#[cfg(target_arch = "wasm32")]
pub fn wasm() -> Self {
Self {
device_type: DeviceType::Wasm,
backend: BackendImpl::Wasm(Arc::new(wasm::WasmBackend::new())),
}
}
pub fn default_device() -> Self {
#[cfg(feature = "cuda")]
{
if let Ok(device) = Self::cuda(0) {
return device;
}
}
#[cfg(all(feature = "metal-backend", target_os = "macos"))]
{
if let Ok(device) = Self::metal() {
return device;
}
}
Self::cpu()
}
pub fn device_type(&self) -> DeviceType {
self.device_type
}
#[allow(dead_code, private_interfaces)]
pub(crate) fn backend(&self) -> &BackendImpl {
&self.backend
}
}
pub trait Backend: Send + Sync {
type Storage: Send + Sync;
fn device_type(&self) -> DeviceType;
fn synchronize(&self) -> Result<()>;
fn zeros(&self, shape: &[usize]) -> Result<Self::Storage>;
fn ones(&self, shape: &[usize]) -> Result<Self::Storage>;
fn from_slice(&self, data: &[f32], shape: &[usize]) -> Result<Self::Storage>;
fn to_vec(&self, storage: &Self::Storage) -> Result<Vec<f32>>;
fn shape(&self, storage: &Self::Storage) -> Vec<usize>;
fn add(&self, a: &Self::Storage, b: &Self::Storage) -> Result<Self::Storage>;
fn sub(&self, a: &Self::Storage, b: &Self::Storage) -> Result<Self::Storage>;
fn mul(&self, a: &Self::Storage, b: &Self::Storage) -> Result<Self::Storage>;
fn matmul(&self, a: &Self::Storage, b: &Self::Storage) -> Result<Self::Storage>;
fn relu(&self, a: &Self::Storage) -> Result<Self::Storage>;
fn sigmoid(&self, a: &Self::Storage) -> Result<Self::Storage>;
fn exp(&self, a: &Self::Storage) -> Result<Self::Storage>;
fn log(&self, a: &Self::Storage) -> Result<Self::Storage>;
fn powf(&self, a: &Self::Storage, power: f32) -> Result<Self::Storage>;
fn softmax(&self, a: &Self::Storage) -> Result<Self::Storage>;
fn sum(&self, a: &Self::Storage) -> Result<Self::Storage>;
fn sum_axis(&self, a: &Self::Storage, axis: usize) -> Result<Self::Storage>;
fn reshape(&self, a: &Self::Storage, new_shape: &[usize]) -> Result<Self::Storage>;
fn transpose(&self, a: &Self::Storage, axis1: usize, axis2: usize) -> Result<Self::Storage>;
fn embedding(&self, indices: &Self::Storage, weights: &Self::Storage) -> Result<Self::Storage>;
fn layer_norm(
&self,
x: &Self::Storage,
gamma: &Self::Storage,
beta: &Self::Storage,
epsilon: f32,
) -> Result<Self::Storage>;
fn sparse_cross_entropy(
&self,
logits: &Self::Storage,
targets: &Self::Storage,
) -> Result<Self::Storage>;
}
pub enum Storage {
Cpu(ArrayD<f32>),
#[cfg(feature = "cuda")]
Cuda(cuda::CudaStorage),
#[cfg(feature = "metal")]
Metal(metal::MetalStorage),
#[cfg(target_arch = "wasm32")]
Wasm(wasm::WasmStorage),
}
impl Storage {
pub fn shape(&self) -> &[usize] {
match self {
Storage::Cpu(arr) => arr.shape(),
#[cfg(feature = "cuda")]
Storage::Cuda(storage) => &storage.shape,
#[cfg(feature = "metal")]
Storage::Metal(storage) => &storage.shape,
#[cfg(target_arch = "wasm32")]
Storage::Wasm(storage) => &storage.shape,
}
}
}