1use cubecl::prelude::{CubeElement, CubePrimitive};
2use std::any::TypeId;
3use std::marker::PhantomData;
4use std::rc::Rc;
5use tenferro_tensor::backend::{
6 BackendSession, BackendSessionHost, ElementwiseFusionPlan, GroupedGemmConfig, SessionCachedDot,
7 TensorAnalytic, TensorBuffer, TensorDeviceTransfer, TensorDot, TensorElementwise, TensorFusion,
8 TensorIndexing, TensorReduction, TensorStructural,
9};
10use tenferro_tensor::config::{
11 CompareDir, DotGeneralConfig, GatherConfig, PadConfig, ScatterConfig, SliceConfig,
12};
13use tenferro_tensor::{
14 with_session_entry_guard, TensorRank, TensorScalar, TensorViewCanonicalization, TypedTensorView,
15};
16use tenferro_tensor::{
17 DotGeneralAccumulation, Tensor, TensorRead, TensorValue, TensorWrite, TypedTensor,
18};
19
20use super::identity::GpuExtensionCapability;
21use super::runtime::RawContextRestore;
22use super::{
23 raw, session_cubecl, CudaBackend, CudaDeviceInfo, CudaExtensionCache, CudaRuntime,
24 CudaRuntimeIdentity,
25};
26
27struct CubeclExitFlush<'a> {
33 op: &'static str,
34 client: &'a cubecl::client::ComputeClient<cubecl_cuda::CudaRuntime>,
35 flushed: bool,
36}
37
38impl<'a> CubeclExitFlush<'a> {
39 fn new(
40 op: &'static str,
41 client: &'a cubecl::client::ComputeClient<cubecl_cuda::CudaRuntime>,
42 ) -> Self {
43 Self {
44 op,
45 client,
46 flushed: false,
47 }
48 }
49
50 fn flush_now(&mut self) -> crate::Result<()> {
52 self.client
53 .flush()
54 .map_err(|err| crate::Error::backend_source(self.op, err))?;
55 self.flushed = true;
56 Ok(())
57 }
58}
59
60impl Drop for CubeclExitFlush<'_> {
61 fn drop(&mut self) {
62 if !self.flushed {
63 let _ = self.client.flush();
64 }
65 }
66}
67
68#[doc(hidden)]
70pub(super) struct CudaExecSessionMarker;
71
72#[derive(Debug)]
85pub struct CudaExecSession<'a> {
86 backend: &'a mut CudaBackend,
87 _not_send_sync: PhantomData<Rc<()>>,
88}
89
90impl CudaExecSession<'_> {
91 pub fn runtime(&self) -> &CudaRuntime {
93 self.backend.runtime()
94 }
95
96 pub fn runtime_identity(&self) -> CudaRuntimeIdentity {
98 self.backend.runtime_identity()
99 }
100
101 pub fn supports(&self, capability: GpuExtensionCapability) -> bool {
116 self.backend.runtime().supports_extension(capability)
117 }
118
119 pub fn device_info(&self) -> &CudaDeviceInfo {
134 self.backend.runtime().device_info()
135 }
136
137 pub fn allocation_domain(&self) -> tenferro_tensor::AllocationDomainId {
152 self.backend.runtime().allocation_domain()
153 }
154
155 pub fn ensure_gpu_resident(&self, input: &Tensor, op: &'static str) -> crate::Result<()> {
181 match input {
182 Tensor::F32(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
183 Tensor::F64(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
184 Tensor::I32(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
185 Tensor::I64(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
186 Tensor::Bool(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
187 Tensor::C32(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
188 Tensor::C64(t) => super::dispatch::ensure_resident_on_runtime(self.runtime(), t, op),
189 }
190 }
191
192 pub fn synchronize(&mut self) -> crate::Result<()> {
202 self.backend.runtime().synchronize()
203 }
204
205 pub fn with_raw<R>(
240 &mut self,
241 op: &'static str,
242 f: impl for<'s> FnOnce(&mut raw::Session<'s>) -> crate::Result<R>,
243 ) -> crate::Result<R> {
244 let runtime = self.backend.runtime().clone();
245 let cache = self.backend.cuda_extension_cache();
246 let stream = runtime.raw_cuda_stream()?;
248 runtime.flush_cubecl(op)?;
250 let device_ordinal = i32::try_from(runtime.device_ordinal())
252 .map_err(|source| crate::Error::backend_source(op, source))?;
253 let _guard = RawContextRestore::enter(op, device_ordinal, runtime.primary_context())?;
254 let mut session = unsafe { raw::Session::new(runtime, cache, stream) };
259 f(&mut session)
260 }
261
262 pub fn with_cubecl<R>(
285 &mut self,
286 op: &'static str,
287 f: impl for<'s> FnOnce(&session_cubecl::Session<'s>) -> crate::Result<R>,
288 ) -> crate::Result<R> {
289 let runtime = self.backend.runtime().clone();
290 runtime.flush_cubecl(op)?;
291 let session = unsafe { session_cubecl::Session::new(runtime) };
292 let mut _flush_guard = CubeclExitFlush::new(op, session.client());
294 let result = f(&session);
295 let flush_result = _flush_guard.flush_now();
296 match result {
297 Ok(value) => {
298 flush_result?;
299 Ok(value)
300 }
301 Err(err) => {
302 let _ = flush_result;
303 Err(err)
304 }
305 }
306 }
307
308 #[doc(hidden)]
309 pub fn tril_typed<T>(&self, input: &TypedTensor<T>, k: i64) -> crate::Result<TypedTensor<T>>
310 where
311 T: CubeElement + TensorScalar + CubePrimitive + Clone,
312 {
313 self.backend.tril_typed(input, k)
314 }
315
316 #[doc(hidden)]
317 pub fn slice_typed<T>(
318 &self,
319 input: &TypedTensor<T>,
320 config: &SliceConfig,
321 ) -> crate::Result<TypedTensor<T>>
322 where
323 T: CubeElement + TensorScalar + CubePrimitive + Clone,
324 {
325 self.backend.slice_typed(input, config)
326 }
327
328 #[doc(hidden)]
330 pub fn cuda_extension_cache(&self) -> &CudaExtensionCache {
331 self.backend.cuda_extension_cache()
332 }
333
334 #[doc(hidden)]
335 pub fn triu_typed<T>(&self, input: &TypedTensor<T>, k: i64) -> crate::Result<TypedTensor<T>>
336 where
337 T: CubeElement + TensorScalar + CubePrimitive + Clone,
338 {
339 self.backend.triu_typed(input, k)
340 }
341
342 #[doc(hidden)]
343 pub fn to_contiguous<T, R>(
344 &mut self,
345 view: &TypedTensorView<'_, T, R>,
346 ) -> crate::Result<TypedTensor<T, R>>
347 where
348 T: TensorScalar,
349 R: TensorRank,
350 CudaBackend: TensorViewCanonicalization<T, R>,
351 {
352 self.backend.to_contiguous(view)
353 }
354}
355
356pub fn with_cuda_exec_session<B, R>(
378 session: &mut B,
379 f: impl for<'a> FnOnce(&'a mut CudaExecSession<'a>) -> R,
380) -> Option<R>
381where
382 B: BackendSession + ?Sized,
383{
384 if session.session_type_id() != std::any::TypeId::of::<CudaExecSessionMarker>() {
385 return None;
386 }
387 let data = unsafe { session.session_data_mut() };
388 Some(unsafe { f(&mut *(data.cast::<CudaExecSession<'static>>())) })
391}
392
393macro_rules! delegate {
394 ($trait:path {
395 $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) -> $ret:ty;)*
396 }) => {
397 impl $trait for CudaExecSession<'_> {
398 $(
399 fn $method(&mut self, $($arg: $arg_ty),*) -> $ret {
400 self.backend.$method($($arg),*)
401 }
402 )*
403 }
404 };
405}
406
407delegate!(TensorElementwise {
408 fn add(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
409 fn sub(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
410 fn mul(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
411 fn neg(input: &Tensor) -> crate::Result<Tensor>;
412 fn conj(input: &Tensor) -> crate::Result<Tensor>;
413 fn div(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
414 fn rem(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
415 fn abs(input: &Tensor) -> crate::Result<Tensor>;
416 fn sign(input: &Tensor) -> crate::Result<Tensor>;
417 fn maximum(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
418 fn minimum(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
419 fn compare(lhs: &Tensor, rhs: &Tensor, dir: &CompareDir) -> crate::Result<Tensor>;
420 fn select(pred: &Tensor, on_true: &Tensor, on_false: &Tensor) -> crate::Result<Tensor>;
421 fn clamp(input: &Tensor, lower: &Tensor, upper: &Tensor) -> crate::Result<Tensor>;
422});
423
424delegate!(TensorAnalytic {
425 fn exp(input: &Tensor) -> crate::Result<Tensor>;
426 fn log(input: &Tensor) -> crate::Result<Tensor>;
427 fn sin(input: &Tensor) -> crate::Result<Tensor>;
428 fn cos(input: &Tensor) -> crate::Result<Tensor>;
429 fn tanh(input: &Tensor) -> crate::Result<Tensor>;
430 fn sqrt(input: &Tensor) -> crate::Result<Tensor>;
431 fn rsqrt(input: &Tensor) -> crate::Result<Tensor>;
432 fn pow(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
433 fn expm1(input: &Tensor) -> crate::Result<Tensor>;
434 fn log1p(input: &Tensor) -> crate::Result<Tensor>;
435});
436
437delegate!(TensorStructural {
438 fn to_contiguous_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
439 fn copy_read_into(src: TensorRead<'_>, dst: TensorWrite<'_>) -> crate::Result<()>;
440 fn transpose(input: &Tensor, perm: &[usize]) -> crate::Result<Tensor>;
441 fn reshape(input: &Tensor, shape: &[usize]) -> crate::Result<Tensor>;
442 fn broadcast_in_dim(input: &Tensor, shape: &[usize], dims: &[usize]) -> crate::Result<Tensor>;
443 fn cast(input: &Tensor, to: tenferro_tensor::DType) -> crate::Result<Tensor>;
444 fn extract_diagonal(input: &Tensor, axis_a: usize, axis_b: usize) -> crate::Result<Tensor>;
445 fn embed_diagonal(input: &Tensor, axis_a: usize, axis_b: usize) -> crate::Result<Tensor>;
446 fn tril(input: &Tensor, k: i64) -> crate::Result<Tensor>;
447 fn triu(input: &Tensor, k: i64) -> crate::Result<Tensor>;
448});
449
450delegate!(TensorReduction {
451 fn reduce_sum(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
452 fn reduce_sum_squares_read(input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor>;
453 fn reduce_prod(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
454 fn reduce_max(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
455 fn reduce_min(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
456});
457
458delegate!(TensorDot {
459 fn dot_general(lhs: &Tensor, rhs: &Tensor, config: &DotGeneralConfig) -> crate::Result<Tensor>;
460 fn dot_general_with_conj(
461 lhs: &Tensor,
462 rhs: &Tensor,
463 config: &DotGeneralConfig,
464 lhs_conj: bool,
465 rhs_conj: bool,
466 ) -> crate::Result<Tensor>;
467 fn dot_general_read_into_accum(
468 lhs: TensorRead<'_>,
469 rhs: TensorRead<'_>,
470 config: &DotGeneralConfig,
471 accumulation: DotGeneralAccumulation,
472 out: TensorWrite<'_>,
473 ) -> crate::Result<()>;
474});
475
476delegate!(TensorIndexing {
477 fn gather(
478 operand: &Tensor,
479 start_indices: &Tensor,
480 config: &GatherConfig,
481 ) -> crate::Result<Tensor>;
482 fn scatter(
483 operand: &Tensor,
484 scatter_indices: &Tensor,
485 updates: &Tensor,
486 config: &ScatterConfig,
487 ) -> crate::Result<Tensor>;
488 fn slice(input: &Tensor, config: &SliceConfig) -> crate::Result<Tensor>;
489 fn dynamic_slice(
490 input: &Tensor,
491 starts: &Tensor,
492 slice_sizes: &[usize],
493 ) -> crate::Result<Tensor>;
494 fn dynamic_update_slice(
495 operand: &Tensor,
496 update: &Tensor,
497 starts: &Tensor,
498 ) -> crate::Result<Tensor>;
499 fn pad(input: &Tensor, config: &PadConfig) -> crate::Result<Tensor>;
500 fn concatenate(inputs: &[&Tensor], axis: usize) -> crate::Result<Tensor>;
501 fn reverse(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
502});
503
504delegate!(TensorFusion {
505 fn execute_elementwise_fusion(
506 inputs: &[&Tensor],
507 plan: &ElementwiseFusionPlan,
508 ) -> crate::Result<Option<Vec<Tensor>>>;
509 fn execute_broadcast_multiply(
510 lhs: TensorRead<'_>,
511 lhs_shape: &[usize],
512 lhs_dims: &[usize],
513 rhs: TensorRead<'_>,
514 rhs_shape: &[usize],
515 rhs_dims: &[usize],
516 ) -> crate::Result<Option<Tensor>>;
517 fn execute_broadcast_multiply_value(
518 lhs: TensorRead<'_>,
519 lhs_shape: &[usize],
520 lhs_dims: &[usize],
521 rhs: TensorRead<'_>,
522 rhs_shape: &[usize],
523 rhs_dims: &[usize],
524 ) -> crate::Result<Option<TensorValue>>;
525});
526
527delegate!(TensorBuffer {
528 fn reclaim_buffer(tensor: Tensor) -> ();
529});
530
531delegate!(TensorDeviceTransfer {
532 fn download_to_host(tensor: TensorRead<'_>) -> crate::Result<Tensor>;
533 fn upload_host_tensor(tensor: TensorRead<'_>) -> crate::Result<Tensor>;
534});
535
536macro_rules! delegate_cached {
537 ($(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) -> $ret:ty;)*) => {
538 impl SessionCachedDot for CudaExecSession<'_> {
539 $(
540 fn $method(&mut self, $($arg: $arg_ty),*) -> $ret {
541 <CudaBackend as SessionCachedDot>::$method(self.backend, $($arg),*)
542 }
543 )*
544 }
545 };
546}
547
548delegate_cached! {
549 fn dot_general_cached(
550 cache_slot: Option<usize>,
551 lhs: &Tensor,
552 rhs: &Tensor,
553 config: &DotGeneralConfig,
554 ) -> crate::Result<Tensor>;
555 fn dot_general_read_cached(
556 cache_slot: Option<usize>,
557 lhs: TensorRead<'_>,
558 rhs: TensorRead<'_>,
559 config: &DotGeneralConfig,
560 ) -> crate::Result<Tensor>;
561 fn dot_general_with_conj_cached(
562 cache_slot: Option<usize>,
563 lhs: &Tensor,
564 rhs: &Tensor,
565 config: &DotGeneralConfig,
566 lhs_conj: bool,
567 rhs_conj: bool,
568 ) -> crate::Result<Tensor>;
569 fn dot_general_with_conj_read_cached(
570 cache_slot: Option<usize>,
571 lhs: TensorRead<'_>,
572 rhs: TensorRead<'_>,
573 config: &DotGeneralConfig,
574 lhs_conj: bool,
575 rhs_conj: bool,
576 ) -> crate::Result<Tensor>;
577 fn dot_general_read_into_accum_cached(
578 cache_slot: Option<usize>,
579 lhs: TensorRead<'_>,
580 rhs: TensorRead<'_>,
581 config: &DotGeneralConfig,
582 accumulation: DotGeneralAccumulation,
583 out: TensorWrite<'_>,
584 ) -> crate::Result<()>;
585 fn grouped_gemm_cached(
586 cache_slot: Option<usize>,
587 lhs: TensorRead<'_>,
588 rhs: TensorRead<'_>,
589 config: &GroupedGemmConfig<'_>,
590 out: TensorWrite<'_>,
591 ) -> crate::Result<()>;
592}
593
594impl BackendSession for CudaExecSession<'_> {
595 fn session_type_id(&self) -> TypeId {
596 TypeId::of::<CudaExecSessionMarker>()
597 }
598
599 unsafe fn session_data_mut(&mut self) -> *mut () {
600 self as *mut Self as *mut ()
601 }
602}
603
604impl BackendSessionHost for CudaBackend {
605 fn with_backend_session<R: Send>(
606 &mut self,
607 f: impl FnOnce(&mut dyn BackendSession) -> R + Send,
608 ) -> R {
609 let mut session = CudaExecSession {
610 backend: self,
611 _not_send_sync: PhantomData,
612 };
613 with_session_entry_guard(|| f(&mut session))
616 }
617}