Skip to main content

candela/tensor/backend/
mod.rs

1mod common;
2mod common_kernels;
3#[cfg(feature = "mkl")]
4mod cpu_mkl;
5mod cpu_pure;
6
7use std::fmt::Debug;
8
9use crate::tensor::mem_formats::layout::Layout;
10use crate::tensor::ops::def_op::OpKind;
11use crate::tensor::storage::TensorData;
12use crate::tensor::traits::Numeric;
13
14/// Scalar element type the tensor framework supports.
15pub trait Dtype: Copy + Numeric {}
16impl Dtype for f32 {}
17impl Dtype for f64 {}
18
19/// Per-`(T, B)` compute dispatch. Splitting kernels across this trait lets each
20/// dtype specialize independently without trait-coherence conflicts; the
21/// [`Backend`] methods delegate here.
22pub trait ComputeFor<B: Backend>: Dtype {
23    fn compute(
24        op: &OpKind<Self>,
25        output_buffer: Vec<Self>,
26        output_layout: &Layout,
27        inputs: &[TensorData<Self>],
28    ) -> TensorData<Self>;
29
30    fn compute_inplace(
31        op: &OpKind<Self>,
32        output_layout: &Layout,
33        inputs: Vec<TensorData<Self>>,
34        output_idx: usize,
35    ) -> TensorData<Self>;
36}
37
38/// Compute strategy used by [`Tensor`](crate::tensor::Tensor) and the planner
39/// to execute graph nodes. A `Backend` impl is a zero-sized policy type; all
40/// state lives in the `TensorData` buffers passed through `compute`.
41///
42/// # Required behaviour
43///
44/// Implementations must accept stride-0 batch axes in
45/// `OpKind::MatMul`. A batched
46/// matmul whose leading axis has stride 0 is computed by re-reading the same
47/// matrix per batch iteration; the op layer relies on this to skip
48/// materializing batch broadcasts.
49pub trait Backend: Sized + Debug {
50    /// `true` if `OpKind::MatMul`
51    /// accepts a 2D input whose strides describe a transposed view
52    /// (`row_stride == 1`, `col_stride > 1`) - i.e. the underlying GEMM is
53    /// invoked with a trans-flag and no copy is required. The fast path is
54    /// deliberately scoped to rank 2; higher-rank tensors are always
55    /// contiguified by the op layer before reaching the kernel.
56    ///
57    /// When `false`, the op layer inserts an `AsContiguous` on any matmul
58    /// input that is not already contiguous.
59    const SUPPORTS_2D_TRANSPOSED_MATMUL: bool = Self::SUPPORTS_NON_CONTIGUOUS_MATMUL;
60    /// `true` if `OpKind::MatMul`
61    /// accepts any memory configuration as long the last 2 axis are contiguous.
62    ///
63    /// When `false`, the op layer inserts an `AsContiguous` on any matmul
64    /// input that is not already contiguous.
65    const SUPPORTS_NON_CONTIGUOUS_MATMUL: bool;
66
67    /// Run `op` over `inputs` into a fresh allocation. `output_buffer` is the
68    /// destination `Vec<T>`; the returned `TensorData` wraps it with
69    /// `output_layout`.
70    fn compute<T>(
71        op: &OpKind<T>,
72        output_buffer: Vec<T>,
73        output_layout: &Layout,
74        inputs: &[TensorData<T>],
75    ) -> TensorData<T>
76    where
77        T: Dtype + ComputeFor<Self>;
78
79    /// Run `op` reusing `inputs[output_idx]`'s buffer as the destination. The
80    /// planner guarantees that buffer is no longer referenced by any live
81    /// node at this point, so the in-place write is sound.
82    fn compute_inplace<T>(
83        op: &OpKind<T>,
84        output_layout: &Layout,
85        inputs: Vec<TensorData<T>>,
86        output_idx: usize,
87    ) -> TensorData<T>
88    where
89        T: Dtype + ComputeFor<Self>;
90}
91
92/// Backend selected when no explicit type parameter is supplied at the
93/// [`Tensor`](crate::tensor::Tensor) construction site. Defaults to the
94/// pure-Rust backend; enabling the `mkl` feature switches it to the Intel MKL
95/// backend.
96///
97/// # Examples
98///
99/// ```
100/// use candela::backend::DefaultBackend;
101/// use candela::Tensor;
102///
103/// // `Tensor<T>` is shorthand for `Tensor<T, DefaultBackend>` - the same type.
104/// let a: Tensor<f64> = Tensor::from_scalar(1.0, &[3]);
105/// let b: Tensor<f64, DefaultBackend> = Tensor::from_scalar(1.0, &[3]);
106/// assert_eq!(a.data(), b.data());
107/// ```
108#[cfg(feature = "mkl")]
109pub type DefaultBackend = cpu_mkl::CpuMkl;
110#[cfg(not(feature = "mkl"))]
111pub type DefaultBackend = cpu_pure::CpuPure;
112
113pub mod implementation {
114    #[cfg(feature = "mkl")]
115    pub use crate::tensor::backend::cpu_mkl::CpuMkl;
116    pub use crate::tensor::backend::cpu_pure::CpuPure;
117}