Skip to main content

tenferro_gpu/cubecl/
session_cubecl.rs

1//! Public tenferro-wide CubeCL session (issue #1597).
2//!
3//! [`Session`] is the promoted, public view over the existing owner-scoped
4//! CubeCL integration helpers in [`super::interop`]. While a session is alive,
5//! the wrapped `ComputeClient` is the exact tenferro CubeCL client bound to the
6//! tenferro `CudaRuntime`; work enqueued through it is flushed at session exit.
7//!
8//! The prelude is intentionally narrow: only the types needed to write and
9//! launch CubeCL kernels are re-exported. Downstream crates must depend on the
10//! framework's `t4a-cubecl` package (see the design doc) to name `#[cube]`
11//! kernels; this module does **not** re-export the whole of `cubecl`.
12
13use std::marker::PhantomData;
14use std::rc::Rc;
15
16use cubecl::client::ComputeClient;
17use cubecl::prelude::{ArrayArg, CubeCount, CubeDim, CubeElement, CubePrimitive, TensorBinding};
18use cubecl_cuda::CudaRuntime as CubeclCudaRuntime;
19
20use tenferro_tensor::{TensorRank, TensorScalar, TensorWrite, TypedTensor};
21
22use super::runtime::CudaRuntime;
23
24/// Public CubeCL extension session.
25///
26/// Not constructible by users; obtained only from
27/// [`CudaExecSession::with_cubecl`](super::exec_session::CudaExecSession::with_cubecl).
28/// The session borrows the exact tenferro CubeCL client for the request scope
29/// and is `!Send + !Sync` by construction, so the execution authority cannot
30/// migrate to another thread.
31pub struct Session<'s> {
32    runtime: CudaRuntime,
33    _scope: PhantomData<&'s ()>,
34    _not_send_sync: PhantomData<Rc<()>>,
35}
36
37impl<'s> Session<'s> {
38    /// Wrap the runtime for one scoped callback.
39    ///
40    /// # Safety
41    ///
42    /// Caller must ensure the callback runs on a thread that owns the tenferro
43    /// primary context activation for `runtime`, and that the returned borrow
44    /// does not outlive the current context.
45    pub(crate) unsafe fn new(runtime: CudaRuntime) -> Self {
46        Self {
47            runtime,
48            _scope: PhantomData,
49            _not_send_sync: PhantomData,
50        }
51    }
52
53    /// Borrow the tenferro CubeCL client.
54    pub fn client(&self) -> &ComputeClient<CubeclCudaRuntime> {
55        self.runtime.client()
56    }
57
58    /// Build a CubeCL tensor binding for a GPU-backed tensor.
59    ///
60    /// # Errors
61    ///
62    /// Returns [`crate::Error::RuntimeState`] when the tensor is not resident
63    /// on this session's runtime/device, or [`crate::Error::Validation`] when
64    /// its layout cannot be bound.
65    pub fn tensor_binding<T>(
66        &self,
67        tensor: &TypedTensor<T, impl TensorRank>,
68        op: &'static str,
69    ) -> crate::Result<TensorBinding<CubeclCudaRuntime>>
70    where
71        T: CubeElement + TensorScalar + Clone,
72    {
73        super::dispatch::ensure_resident_on_runtime(&self.runtime, tensor, op)?;
74        super::interop::typed_tensor_binding(tensor, op)
75    }
76
77    /// Build a CubeCL array argument for a GPU-backed tensor.
78    ///
79    /// # Errors
80    ///
81    /// Returns [`crate::Error::RuntimeState`] when the tensor is not resident
82    /// on this session's runtime/device, or [`crate::Error::Validation`] when
83    /// its layout cannot be bound.
84    pub fn array_arg<T>(
85        &self,
86        tensor: &TypedTensor<T, impl TensorRank>,
87        op: &'static str,
88    ) -> crate::Result<ArrayArg<CubeclCudaRuntime>>
89    where
90        T: CubeElement + TensorScalar + Clone,
91    {
92        super::dispatch::ensure_resident_on_runtime(&self.runtime, tensor, op)?;
93        super::interop::typed_tensor_array_arg(tensor, op)
94    }
95
96    /// Allocate a dense GPU tensor on the session's device.
97    ///
98    /// # Errors
99    ///
100    /// Returns [`crate::Error::Validation`] when the shape product overflows,
101    /// or [`crate::Error::BackendSource`] when allocation fails.
102    pub fn alloc_output<T>(&self, shape: &[usize]) -> crate::Result<TypedTensor<T>>
103    where
104        T: CubeElement + TensorScalar + Clone + Send + Sync + 'static,
105    {
106        super::interop::alloc_output(&self.runtime, shape)
107    }
108
109    /// Allocate a dense GPU tensor zero-filled with the session's device.
110    ///
111    /// Reuses the backend's fill-zero structural kernel; it never uploads a
112    /// host tensor or exposes a device pointer to the caller.
113    ///
114    /// # Errors
115    ///
116    /// Returns [`crate::Error::Validation`] when the shape product, output
117    /// byte length, or launch count overflows, [`crate::Error::RuntimeState`]
118    /// when the output is not resident, or [`crate::Error::BackendSource`]
119    /// when allocation or backend resource inspection fails.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use tenferro_gpu::cuda::cubecl::Session;
125    ///
126    /// fn check(session: &Session<'_>) -> tenferro_tensor::Result<()> {
127    ///     let _ = session.alloc_zero_output::<f32>(&[4])?;
128    ///     Ok(())
129    /// }
130    /// ```
131    pub fn alloc_zero_output<T>(&self, shape: &[usize]) -> crate::Result<TypedTensor<T>>
132    where
133        T: CubeElement + CubePrimitive + TensorScalar + Clone + Send + Sync + 'static,
134    {
135        super::interop::alloc_zero_output(&self.runtime, shape)
136    }
137
138    /// Scale a mutable CUDA tensor in place by a real factor.
139    ///
140    /// Supports F32, F64, C32, and C64 payloads; the factor is interpreted as
141    /// a real scalar for complex payloads. This is the op-family scale
142    /// primitive used for normalization after a vendor transform; it never
143    /// exposes a device pointer to the caller.
144    ///
145    /// # Errors
146    ///
147    /// Returns [`crate::Error::RuntimeState`] when the tensor is not resident
148    /// on this session's runtime, a typed layout error for a non-zero-offset
149    /// discontinuous view, or the typed unsupported-dtype error for other
150    /// payloads.
151    pub fn scale_tensor_write(&self, output: TensorWrite<'_>, factor: f64) -> crate::Result<()> {
152        super::interop::scale_tensor_write(&self.runtime, output, factor)
153    }
154
155    /// Return the cube count for a one-dimensional kernel domain of `len`.
156    ///
157    /// # Errors
158    ///
159    /// Returns [`crate::Error::Validation`] carrying
160    /// `ValidationError::InvalidArgument` when the one-dimensional launch for
161    /// `len` elements would require more than `u32::MAX` workgroups.
162    pub fn cube_count_1d(&self, len: usize) -> crate::Result<CubeCount> {
163        super::interop::cube_count_for_len(len)
164    }
165
166    /// Return the standard one-dimensional CubeCL launch dimension.
167    pub fn cube_dim_1d(&self) -> CubeDim {
168        super::interop::cube_dim_1d()
169    }
170}