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, ElementwiseReadOp,
7 GroupedGemmConfig, SessionCachedDot, TensorAnalytic, TensorBuffer, TensorDeviceTransfer,
8 TensorDot, TensorElementwise, TensorFusion, 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_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
409 fn sub_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
410 fn mul_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
411 fn neg_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
412 fn conj_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
413 fn div_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
414 fn rem_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
415 fn abs_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
416 fn sign_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
417 fn maximum_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
418 fn minimum_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
419 fn compare_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>, dir: &CompareDir) -> crate::Result<Tensor>;
420 fn select_read(pred: TensorRead<'_>, on_true: TensorRead<'_>, on_false: TensorRead<'_>) -> crate::Result<Tensor>;
421 fn clamp_read(input: TensorRead<'_>, lower: TensorRead<'_>, upper: TensorRead<'_>) -> crate::Result<Tensor>;
422 fn elementwise_read_into(op: ElementwiseReadOp, inputs: &[TensorRead<'_>], out: TensorWrite<'_>) -> crate::Result<()>;
423 fn add(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
424 fn sub(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
425 fn mul(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
426 fn neg(input: &Tensor) -> crate::Result<Tensor>;
427 fn conj(input: &Tensor) -> crate::Result<Tensor>;
428 fn div(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
429 fn rem(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
430 fn abs(input: &Tensor) -> crate::Result<Tensor>;
431 fn sign(input: &Tensor) -> crate::Result<Tensor>;
432 fn maximum(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
433 fn minimum(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
434 fn compare(lhs: &Tensor, rhs: &Tensor, dir: &CompareDir) -> crate::Result<Tensor>;
435 fn select(pred: &Tensor, on_true: &Tensor, on_false: &Tensor) -> crate::Result<Tensor>;
436 fn clamp(input: &Tensor, lower: &Tensor, upper: &Tensor) -> crate::Result<Tensor>;
437});
438
439delegate!(TensorAnalytic {
440 fn exp_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
441 fn log_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
442 fn sin_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
443 fn cos_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
444 fn tanh_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
445 fn sqrt_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
446 fn rsqrt_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
447 fn pow_read(lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor>;
448 fn expm1_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
449 fn log1p_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
450 fn exp(input: &Tensor) -> crate::Result<Tensor>;
451 fn log(input: &Tensor) -> crate::Result<Tensor>;
452 fn sin(input: &Tensor) -> crate::Result<Tensor>;
453 fn cos(input: &Tensor) -> crate::Result<Tensor>;
454 fn tanh(input: &Tensor) -> crate::Result<Tensor>;
455 fn sqrt(input: &Tensor) -> crate::Result<Tensor>;
456 fn rsqrt(input: &Tensor) -> crate::Result<Tensor>;
457 fn pow(lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
458 fn expm1(input: &Tensor) -> crate::Result<Tensor>;
459 fn log1p(input: &Tensor) -> crate::Result<Tensor>;
460});
461
462delegate!(TensorStructural {
463 fn transpose_read(input: TensorRead<'_>, perm: &[usize]) -> crate::Result<Tensor>;
464 fn reshape_read(input: TensorRead<'_>, shape: &[usize]) -> crate::Result<Tensor>;
465 fn broadcast_in_dim_read(input: TensorRead<'_>, shape: &[usize], dims: &[usize]) -> crate::Result<Tensor>;
466 fn to_contiguous_read(input: TensorRead<'_>) -> crate::Result<Tensor>;
467 fn copy_read_into(src: TensorRead<'_>, dst: TensorWrite<'_>) -> crate::Result<()>;
468 fn transpose(input: &Tensor, perm: &[usize]) -> crate::Result<Tensor>;
469 fn reshape(input: &Tensor, shape: &[usize]) -> crate::Result<Tensor>;
470 fn broadcast_in_dim(input: &Tensor, shape: &[usize], dims: &[usize]) -> crate::Result<Tensor>;
471 fn cast(input: &Tensor, to: tenferro_tensor::DType) -> crate::Result<Tensor>;
472 fn extract_diagonal(input: &Tensor, axis_a: usize, axis_b: usize) -> crate::Result<Tensor>;
473 fn embed_diagonal(input: &Tensor, axis_a: usize, axis_b: usize) -> crate::Result<Tensor>;
474 fn tril(input: &Tensor, k: i64) -> crate::Result<Tensor>;
475 fn triu(input: &Tensor, k: i64) -> crate::Result<Tensor>;
476});
477
478delegate!(TensorReduction {
479 fn reduce_sum_read(input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor>;
480 fn reduce_prod_read(input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor>;
481 fn reduce_max_read(input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor>;
482 fn reduce_min_read(input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor>;
483 fn reduce_sum(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
484 fn reduce_sum_squares_read(input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor>;
485 fn reduce_prod(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
486 fn reduce_max(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
487 fn reduce_min(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
488});
489
490delegate!(TensorDot {
491 fn dot_general(lhs: &Tensor, rhs: &Tensor, config: &DotGeneralConfig) -> crate::Result<Tensor>;
492 fn dot_general_with_conj(
493 lhs: &Tensor,
494 rhs: &Tensor,
495 config: &DotGeneralConfig,
496 lhs_conj: bool,
497 rhs_conj: bool,
498 ) -> crate::Result<Tensor>;
499 fn dot_general_read_into_accum(
500 lhs: TensorRead<'_>,
501 rhs: TensorRead<'_>,
502 config: &DotGeneralConfig,
503 accumulation: DotGeneralAccumulation,
504 out: TensorWrite<'_>,
505 ) -> crate::Result<()>;
506});
507
508delegate!(TensorIndexing {
509 fn gather(
510 operand: &Tensor,
511 start_indices: &Tensor,
512 config: &GatherConfig,
513 ) -> crate::Result<Tensor>;
514 fn scatter(
515 operand: &Tensor,
516 scatter_indices: &Tensor,
517 updates: &Tensor,
518 config: &ScatterConfig,
519 ) -> crate::Result<Tensor>;
520 fn slice(input: &Tensor, config: &SliceConfig) -> crate::Result<Tensor>;
521 fn dynamic_slice(
522 input: &Tensor,
523 starts: &Tensor,
524 slice_sizes: &[usize],
525 ) -> crate::Result<Tensor>;
526 fn dynamic_update_slice(
527 operand: &Tensor,
528 update: &Tensor,
529 starts: &Tensor,
530 ) -> crate::Result<Tensor>;
531 fn pad(input: &Tensor, config: &PadConfig) -> crate::Result<Tensor>;
532 fn concatenate(inputs: &[&Tensor], axis: usize) -> crate::Result<Tensor>;
533 fn reverse(input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
534});
535
536delegate!(TensorFusion {
537 fn execute_elementwise_fusion(
538 inputs: &[&Tensor],
539 plan: &ElementwiseFusionPlan,
540 ) -> crate::Result<Option<Vec<Tensor>>>;
541 fn execute_broadcast_multiply(
542 lhs: TensorRead<'_>,
543 lhs_shape: &[usize],
544 lhs_dims: &[usize],
545 rhs: TensorRead<'_>,
546 rhs_shape: &[usize],
547 rhs_dims: &[usize],
548 ) -> crate::Result<Option<Tensor>>;
549 fn execute_broadcast_multiply_value(
550 lhs: TensorRead<'_>,
551 lhs_shape: &[usize],
552 lhs_dims: &[usize],
553 rhs: TensorRead<'_>,
554 rhs_shape: &[usize],
555 rhs_dims: &[usize],
556 ) -> crate::Result<Option<TensorValue>>;
557});
558
559delegate!(TensorBuffer {
560 fn reclaim_buffer(tensor: Tensor) -> ();
561});
562
563delegate!(TensorDeviceTransfer {
564 fn download_to_host(tensor: TensorRead<'_>) -> crate::Result<Tensor>;
565 fn upload_host_tensor(tensor: TensorRead<'_>) -> crate::Result<Tensor>;
566});
567
568macro_rules! delegate_cached {
569 ($(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) -> $ret:ty;)*) => {
570 impl SessionCachedDot for CudaExecSession<'_> {
571 $(
572 fn $method(&mut self, $($arg: $arg_ty),*) -> $ret {
573 <CudaBackend as SessionCachedDot>::$method(self.backend, $($arg),*)
574 }
575 )*
576 }
577 };
578}
579
580delegate_cached! {
581 fn dot_general_cached(
582 cache_slot: Option<usize>,
583 lhs: &Tensor,
584 rhs: &Tensor,
585 config: &DotGeneralConfig,
586 ) -> crate::Result<Tensor>;
587 fn dot_general_read_cached(
588 cache_slot: Option<usize>,
589 lhs: TensorRead<'_>,
590 rhs: TensorRead<'_>,
591 config: &DotGeneralConfig,
592 ) -> crate::Result<Tensor>;
593 fn dot_general_with_conj_cached(
594 cache_slot: Option<usize>,
595 lhs: &Tensor,
596 rhs: &Tensor,
597 config: &DotGeneralConfig,
598 lhs_conj: bool,
599 rhs_conj: bool,
600 ) -> crate::Result<Tensor>;
601 fn dot_general_with_conj_read_cached(
602 cache_slot: Option<usize>,
603 lhs: TensorRead<'_>,
604 rhs: TensorRead<'_>,
605 config: &DotGeneralConfig,
606 lhs_conj: bool,
607 rhs_conj: bool,
608 ) -> crate::Result<Tensor>;
609 fn dot_general_read_into_accum_cached(
610 cache_slot: Option<usize>,
611 lhs: TensorRead<'_>,
612 rhs: TensorRead<'_>,
613 config: &DotGeneralConfig,
614 accumulation: DotGeneralAccumulation,
615 out: TensorWrite<'_>,
616 ) -> crate::Result<()>;
617 fn grouped_gemm_cached(
618 cache_slot: Option<usize>,
619 lhs: TensorRead<'_>,
620 rhs: TensorRead<'_>,
621 config: &GroupedGemmConfig<'_>,
622 out: TensorWrite<'_>,
623 ) -> crate::Result<()>;
624}
625
626impl BackendSession for CudaExecSession<'_> {
627 fn vdot_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
628 BackendSession::vdot_read(self.backend, lhs, rhs)
629 }
630
631 fn norm_squared_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
632 BackendSession::norm_squared_read(self.backend, input)
633 }
634
635 fn axpby_read_into_accum(
636 &mut self,
637 alpha: tenferro_tensor::ContractionScalar,
638 x: TensorRead<'_>,
639 beta: tenferro_tensor::ContractionScalar,
640 y: TensorWrite<'_>,
641 ) -> crate::Result<()> {
642 BackendSession::axpby_read_into_accum(self.backend, alpha, x, beta, y)
643 }
644
645 fn session_type_id(&self) -> TypeId {
646 TypeId::of::<CudaExecSessionMarker>()
647 }
648
649 unsafe fn session_data_mut(&mut self) -> *mut () {
650 self as *mut Self as *mut ()
651 }
652}
653
654impl BackendSessionHost for CudaBackend {
655 fn with_backend_session<R: Send>(
656 &mut self,
657 f: impl FnOnce(&mut dyn BackendSession) -> R + Send,
658 ) -> R {
659 let mut session = CudaExecSession {
660 backend: self,
661 _not_send_sync: PhantomData,
662 };
663 with_session_entry_guard(|| f(&mut session))
666 }
667}