1use std::fmt;
2use std::mem::{size_of, size_of_val};
3use std::sync::Arc;
4
5use tenferro_runtime::program::{CoreSemanticOp, SemanticOpRef, SemanticOperationView};
6use tenferro_runtime::{
7 assemble_executable_engine_registration, CacheOwnerError, CoreCapabilityBundle,
8 CoreCapabilityKind, CorePrepareContext, DotGeneralPreparation, DotGeneralPrepareRequest,
9 ElementwisePrepareRequest, ElementwiseRuntime, EngineId, EngineRegistration,
10 EngineRegistrationMetadata, ExecutableEngineRegistrationConfig, ExecutionContextIdentity,
11 HardwareClassId, IndexingPrepareRequest, IndexingRuntime, InputIngressContract,
12 InputPlacementContract, InputSignature, InputSignatureContract, InputSpecializationProjection,
13 InputSpecializationRequirements, LayoutPrepareRequest, LayoutProjection, LayoutRuntime,
14 LayoutSpecialization, PrepareCapability, PrepareError, PreparedOperation,
15 PreparedOperationBinding, PreparedOperationPlan, ProviderContractError, ProviderDeviceIdentity,
16 ProviderId, ReductionPrepareRequest, ReductionRuntime, ResidentOutputContract,
17 RuntimeCacheOwner, RuntimeConfigError, RuntimeInputContract, SpecializationError,
18 SpecializationProjection, SpecializationRequirements, StorageClass, UnsupportedReason,
19};
20use tenferro_tensor::{DeviceKind, GpuBackendKind, MemoryKind, Placement, TensorRead, TensorView};
21
22use super::event_domain::CudaEventDomainDriver;
23use super::CudaBackend;
24
25const CUDA_HARDWARE_CLASS_ID: &str = "tenferro-cuda.device.v1";
26const CUDA_STORAGE_CLASS_ID: &str = "tenferro.storage.device.v1";
27const UNKNOWN_CORE_OPERATION: &str = "unknown-core-operation";
28
29#[derive(Debug)]
30pub(crate) struct PreparedCudaRegistrationIdentity {
31 engine_id: EngineId,
32 provider_device_identity: ProviderDeviceIdentity,
33}
34
35pub(crate) fn prepare_cuda_registration_identity(
36 engine_id: EngineId,
37 device_id: super::CudaDeviceId,
38) -> Result<PreparedCudaRegistrationIdentity, RuntimeConfigError> {
39 Ok(PreparedCudaRegistrationIdentity {
40 engine_id,
41 provider_device_identity: cuda_provider_device_identity(device_id)?,
42 })
43}
44
45pub fn cuda_runtime_hardware_class() -> Result<HardwareClassId, RuntimeConfigError> {
52 HardwareClassId::new(CUDA_HARDWARE_CLASS_ID).map_err(RuntimeConfigError::from)
53}
54
55pub fn cuda_runtime_engine_registration(
69 backend: &CudaBackend,
70 engine_id: EngineId,
71) -> Result<EngineRegistration, RuntimeConfigError> {
72 let prepared_identity = prepare_cuda_registration_identity(engine_id, backend.device_id())?;
73 let backend = Arc::new(backend.clone());
74 let elementwise: Arc<dyn ElementwiseRuntime> = backend.clone();
75 let reduction: Arc<dyn ReductionRuntime> = backend.clone();
76 let indexing: Arc<dyn IndexingRuntime> = backend.clone();
77 let dot_general: Arc<dyn DotGeneralPreparation> = backend.clone();
78 let layout: Arc<dyn LayoutRuntime> = backend.clone();
79 let cache_owner: Arc<dyn RuntimeCacheOwner> = backend.clone();
80 let execution_backend = backend.as_ref().clone();
81
82 let mut capabilities = CoreCapabilityBundle::builder();
83 capabilities
84 .elementwise(elementwise)
85 .reduction(reduction)
86 .indexing(indexing)
87 .dot_general(dot_general)
88 .layout(layout);
89
90 let storage = cuda_runtime_storage_class()?;
91 let default_storage = storage.clone();
92 let placement_storage = storage.clone();
93 let signature_storage = storage.clone();
94 let runtime_storage = storage.clone();
95 let resident_storage = storage.clone();
96 let device_ordinal = backend.device_id().ordinal() as usize;
97 let allocation_domain = backend.runtime().allocation_domain_id();
98 let ingress = InputIngressContract::new(
99 InputPlacementContract::new(move |placement, candidate| {
100 candidate == &placement_storage && cuda_input_placement(placement, device_ordinal)
101 }),
102 InputSignatureContract::new(move |placement, family, domain, candidate| {
103 candidate == &signature_storage
104 && cuda_input_signature(
105 placement,
106 family,
107 domain,
108 device_ordinal,
109 allocation_domain,
110 )
111 }),
112 RuntimeInputContract::new(move |input: &TensorRead<'_>, candidate| {
113 candidate == &runtime_storage
114 && cuda_input_tensor(input, device_ordinal, allocation_domain)
115 }),
116 ResidentOutputContract::new(move |input: &TensorRead<'_>, candidate| {
117 candidate == &resident_storage
118 && cuda_input_tensor(input, device_ordinal, allocation_domain)
119 }),
120 );
121 let metadata = EngineRegistrationMetadata::new(
122 prepared_identity.engine_id,
123 prepared_identity.provider_device_identity,
124 cuda_runtime_hardware_class()?,
125 Arc::from(vec![storage]),
126 default_storage,
127 capabilities.build(),
128 );
129 assemble_executable_engine_registration(ExecutableEngineRegistrationConfig::new(
130 metadata,
131 execution_backend,
132 Arc::new(CudaEventDomainDriver::new(backend.runtime().clone())),
133 ingress,
134 Some(cache_owner),
135 ))
136}
137
138fn cuda_provider_device_identity(
139 device_id: super::CudaDeviceId,
140) -> Result<ProviderDeviceIdentity, RuntimeConfigError> {
141 Ok(ProviderDeviceIdentity::new(
142 ProviderId::new("tenferro.cuda")?,
143 format!("device:{}", device_id.ordinal()),
144 )?)
145}
146
147fn cuda_input_signature(
148 placement: &Placement,
149 backend_family: Option<&'static str>,
150 allocation_domain: Option<tenferro_tensor::AllocationDomainId>,
151 device_ordinal: usize,
152 expected_domain: tenferro_tensor::AllocationDomainId,
153) -> bool {
154 cuda_input_placement(placement, device_ordinal)
155 && backend_family == Some("cuda")
156 && allocation_domain == Some(expected_domain)
157}
158
159fn cuda_input_placement(placement: &Placement, device_ordinal: usize) -> bool {
160 placement.memory_kind == MemoryKind::Device
161 && matches!(
162 &placement.device,
163 Some(device)
164 if device.kind == DeviceKind::Gpu(GpuBackendKind::Cuda)
165 && device.ordinal == device_ordinal
166 )
167}
168
169fn cuda_input_tensor(
170 input: &TensorRead<'_>,
171 device_ordinal: usize,
172 expected_domain: tenferro_tensor::AllocationDomainId,
173) -> bool {
174 cuda_input_placement(input.placement(), device_ordinal)
178 && input.backend_family() == Some("cuda")
179 && input.allocation_domain() == Some(expected_domain)
180 && cuda_input_has_owned_buffer(input, device_ordinal, expected_domain)
181}
182
183fn cuda_input_has_owned_buffer(
184 input: &TensorRead<'_>,
185 device_ordinal: usize,
186 expected_domain: tenferro_tensor::AllocationDomainId,
187) -> bool {
188 match input.clone().tensor_view() {
189 TensorView::F32(view) => {
190 cubecl_view_has_owner::<f32>(&view, device_ordinal, expected_domain)
191 }
192 TensorView::F64(view) => {
193 cubecl_view_has_owner::<f64>(&view, device_ordinal, expected_domain)
194 }
195 TensorView::I32(view) => {
196 cubecl_view_has_owner::<i32>(&view, device_ordinal, expected_domain)
197 }
198 TensorView::I64(view) => {
199 cubecl_view_has_owner::<i64>(&view, device_ordinal, expected_domain)
200 }
201 TensorView::Bool(view) => {
202 cubecl_view_has_owner::<bool>(&view, device_ordinal, expected_domain)
203 }
204 TensorView::C32(view) => view.backend_buffer().is_some_and(|buffer| {
205 buffer
206 .as_any()
207 .downcast_ref::<crate::CubeclBuffer>()
208 .is_some_and(|buffer| {
209 buffer.device_ordinal() == device_ordinal
210 && buffer.allocation_domain() == expected_domain
211 })
212 }),
213 TensorView::C64(view) => view.backend_buffer().is_some_and(|buffer| {
214 buffer
215 .as_any()
216 .downcast_ref::<crate::CubeclBuffer>()
217 .is_some_and(|buffer| {
218 buffer.device_ordinal() == device_ordinal
219 && buffer.allocation_domain() == expected_domain
220 })
221 }),
222 }
223}
224
225fn cubecl_view_has_owner<T: 'static>(
226 view: &tenferro_tensor::TypedTensorView<'_, T>,
227 device_ordinal: usize,
228 expected_domain: tenferro_tensor::AllocationDomainId,
229) -> bool {
230 view.backend_buffer().is_some_and(|buffer| {
231 buffer
232 .as_any()
233 .downcast_ref::<crate::CubeclBuffer>()
234 .is_some_and(|buffer| {
235 buffer.device_ordinal() == device_ordinal
236 && buffer.allocation_domain() == expected_domain
237 })
238 })
239}
240
241#[cfg(test)]
242#[path = "tests/runtime_adapter.rs"]
243mod tests;
244
245fn cuda_runtime_storage_class() -> Result<StorageClass, RuntimeConfigError> {
246 StorageClass::new(CUDA_STORAGE_CLASS_ID).map_err(RuntimeConfigError::from)
247}
248
249#[derive(Clone, Copy, Debug, Eq, PartialEq)]
250enum CudaPreparedKind {
251 Elementwise,
252 Reduction,
253 Indexing,
254 DotGeneral,
255 Layout,
256}
257
258#[derive(Debug)]
259struct CudaPreparedOperation {
260 binding: PreparedOperationBinding,
261 specialization: SpecializationProjection,
262 #[allow(dead_code, reason = "bounded Debug records the selected CUDA family")]
263 kind: CudaPreparedKind,
264}
265
266impl PreparedOperation for CudaPreparedOperation {
267 fn binding(&self) -> &PreparedOperationBinding {
268 &self.binding
269 }
270
271 fn specialization(&self) -> &SpecializationProjection {
272 &self.specialization
273 }
274
275 fn retained_bytes(&self) -> usize {
276 checked_specialization_heap_retained_bytes(&self.specialization).unwrap_or(usize::MAX)
277 }
278}
279
280impl ElementwiseRuntime for CudaBackend {
281 fn prepare(
282 &self,
283 request: ElementwisePrepareRequest<'_>,
284 ) -> Result<PrepareCapability, PrepareError> {
285 prepare_cuda(
286 request.operation(),
287 request.context(),
288 CudaPreparedKind::Elementwise,
289 )
290 }
291}
292
293impl ReductionRuntime for CudaBackend {
294 fn prepare(
295 &self,
296 request: ReductionPrepareRequest<'_>,
297 ) -> Result<PrepareCapability, PrepareError> {
298 prepare_cuda(
299 request.operation(),
300 request.context(),
301 CudaPreparedKind::Reduction,
302 )
303 }
304}
305
306impl IndexingRuntime for CudaBackend {
307 fn prepare(
308 &self,
309 request: IndexingPrepareRequest<'_>,
310 ) -> Result<PrepareCapability, PrepareError> {
311 prepare_cuda(
312 request.operation(),
313 request.context(),
314 CudaPreparedKind::Indexing,
315 )
316 }
317}
318
319impl DotGeneralPreparation for CudaBackend {
320 fn prepare(
321 &self,
322 request: DotGeneralPrepareRequest<'_>,
323 ) -> Result<PrepareCapability, PrepareError> {
324 prepare_cuda(
325 request.operation(),
326 request.context(),
327 CudaPreparedKind::DotGeneral,
328 )
329 }
330}
331
332impl LayoutRuntime for CudaBackend {
333 fn prepare(
334 &self,
335 request: LayoutPrepareRequest<'_>,
336 ) -> Result<PrepareCapability, PrepareError> {
337 prepare_cuda(
338 request.operation(),
339 request.context(),
340 CudaPreparedKind::Layout,
341 )
342 }
343}
344
345impl RuntimeCacheOwner for CudaBackend {
346 fn cache_stats(&self) -> Result<tenferro_runtime::runtime::CacheStats, CacheOwnerError> {
347 let stats = self
348 .cuda_extension_cache_stats()
349 .map_err(cache_owner_error)?;
350 Ok(tenferro_runtime::runtime::CacheStats {
351 entries: stats.entries,
352 retained_bytes: stats.retained_bytes,
353 hits: stats.hits,
354 misses: stats.misses,
355 evictions: stats.evictions,
356 clears: stats.clears,
357 })
358 }
359
360 fn clear_caches(&self) -> Result<(), CacheOwnerError> {
361 self.clear_cuda_extension_cache().map_err(cache_owner_error)
362 }
363}
364
365fn prepare_cuda(
366 operation: SemanticOperationView<'_>,
367 context: &CorePrepareContext<'_>,
368 expected_kind: CudaPreparedKind,
369) -> Result<PrepareCapability, PrepareError> {
370 validate_cuda_runtime_context(context)?;
371 let SemanticOpRef::Core(op) = operation.op() else {
372 return Err(wrong_family_error(expected_kind, "extension"));
373 };
374 let Some(actual_kind) = cuda_operation_kind(op) else {
375 return Ok(PrepareCapability::Unsupported(
376 UnsupportedReason::Operation {
377 operation: UNKNOWN_CORE_OPERATION,
378 },
379 ));
380 };
381 if actual_kind != expected_kind {
382 return Err(wrong_family_error(expected_kind, core_operation_name(op)));
383 }
384
385 let minimum = minimum_specialization_requirements(actual_kind, context.inputs())?;
386 let merged =
387 merge_specialization_requirements(context.specialization().requirements(), &minimum);
388 if &merged != context.specialization().requirements() {
389 return Ok(PrepareCapability::NeedsSpecialization(merged));
390 }
391
392 Ok(PrepareCapability::Prepared(
393 PreparedOperationPlan::metadata(Arc::new(CudaPreparedOperation {
394 binding: context.binding().clone(),
395 specialization: context.specialization().clone(),
396 kind: actual_kind,
397 })),
398 ))
399}
400
401fn validate_cuda_runtime_context(context: &CorePrepareContext<'_>) -> Result<(), PrepareError> {
402 let expected_context = ExecutionContextIdentity::of::<CudaBackend>();
403 if context.binding().context_identity() != expected_context {
404 return Err(PrepareError::ProviderContract {
405 source: ProviderContractError::WrongOperationFamily {
406 expected: CoreCapabilityKind::Elementwise,
407 operation: "cuda-context-mismatch",
408 },
409 });
410 }
411 if context.binding().hardware_class().as_str() != CUDA_HARDWARE_CLASS_ID {
412 return Err(PrepareError::ProviderContract {
413 source: ProviderContractError::WrongOperationFamily {
414 expected: CoreCapabilityKind::Elementwise,
415 operation: "cuda-hardware-mismatch",
416 },
417 });
418 }
419 if context.resolved_placement().storage_class().as_str() != CUDA_STORAGE_CLASS_ID {
420 return Err(PrepareError::Unsupported {
421 reason: UnsupportedReason::StorageClass {
422 storage_class: context.resolved_placement().storage_class().clone(),
423 },
424 });
425 }
426 Ok(())
427}
428
429fn cuda_operation_kind(op: &CoreSemanticOp) -> Option<CudaPreparedKind> {
430 Some(match op {
431 CoreSemanticOp::Add
432 | CoreSemanticOp::Sub
433 | CoreSemanticOp::Mul
434 | CoreSemanticOp::Neg
435 | CoreSemanticOp::Conj
436 | CoreSemanticOp::Div
437 | CoreSemanticOp::Rem
438 | CoreSemanticOp::Abs
439 | CoreSemanticOp::Sign
440 | CoreSemanticOp::Maximum
441 | CoreSemanticOp::Minimum
442 | CoreSemanticOp::Compare(_)
443 | CoreSemanticOp::Select
444 | CoreSemanticOp::Clamp
445 | CoreSemanticOp::Exp
446 | CoreSemanticOp::Log
447 | CoreSemanticOp::Sin
448 | CoreSemanticOp::Cos
449 | CoreSemanticOp::Tanh
450 | CoreSemanticOp::Sqrt
451 | CoreSemanticOp::Rsqrt
452 | CoreSemanticOp::Pow
453 | CoreSemanticOp::Expm1
454 | CoreSemanticOp::Log1p => CudaPreparedKind::Elementwise,
455 CoreSemanticOp::ReduceSum { .. }
456 | CoreSemanticOp::ReduceSumSquares { .. }
457 | CoreSemanticOp::ReduceProd { .. }
458 | CoreSemanticOp::ReduceMax { .. }
459 | CoreSemanticOp::ReduceMin { .. } => CudaPreparedKind::Reduction,
460 CoreSemanticOp::Gather(_)
461 | CoreSemanticOp::GatherDynamicSliceSizes { .. }
462 | CoreSemanticOp::Scatter(_)
463 | CoreSemanticOp::Slice(_)
464 | CoreSemanticOp::DynamicSlice { .. }
465 | CoreSemanticOp::DynamicUpdateSlice
466 | CoreSemanticOp::Pad(_)
467 | CoreSemanticOp::Concatenate { .. }
468 | CoreSemanticOp::Reverse { .. }
469 | CoreSemanticOp::ShapeOf { .. }
470 | CoreSemanticOp::DynamicTruncate { .. }
471 | CoreSemanticOp::PadToMatch { .. } => CudaPreparedKind::Indexing,
472 CoreSemanticOp::DotGeneral { .. } => CudaPreparedKind::DotGeneral,
473 CoreSemanticOp::Transpose { .. }
474 | CoreSemanticOp::Reshape { .. }
475 | CoreSemanticOp::BroadcastInDim { .. }
476 | CoreSemanticOp::Convert { .. }
477 | CoreSemanticOp::Constant { .. }
478 | CoreSemanticOp::ExtractDiag { .. }
479 | CoreSemanticOp::EmbedDiag { .. }
480 | CoreSemanticOp::Tril { .. }
481 | CoreSemanticOp::Triu { .. } => CudaPreparedKind::Layout,
482 _ => return None,
483 })
484}
485
486fn minimum_specialization_requirements(
487 kind: CudaPreparedKind,
488 inputs: &InputSignature,
489) -> Result<SpecializationRequirements, PrepareError> {
490 let mut requirements = Vec::with_capacity(inputs.entries().len());
491 for (input, entry) in inputs.entries().iter().enumerate() {
492 let mut builder = InputSpecializationRequirements::builder();
493 builder.dtype(true).rank(true);
494 match kind {
495 CudaPreparedKind::Indexing => {
496 builder.concrete_dimensions(concrete_axes_for_rank(input, entry.shape().len())?);
497 }
498 CudaPreparedKind::DotGeneral => {
499 builder
500 .concrete_dimensions(concrete_axes_for_rank(input, entry.shape().len())?)
501 .layout(LayoutSpecialization::Class);
502 }
503 CudaPreparedKind::Elementwise
504 | CudaPreparedKind::Reduction
505 | CudaPreparedKind::Layout => {}
506 }
507 requirements.push(
508 builder
509 .build()
510 .expect("CUDA minimum specialization requirements are internally valid"),
511 );
512 }
513 Ok(SpecializationRequirements::new(requirements))
514}
515
516fn concrete_axes_for_rank(input: usize, rank: usize) -> Result<Vec<u32>, PrepareError> {
517 if u32::try_from(rank).is_err() {
518 return Err(PrepareError::Specialization {
519 source: SpecializationError::ProjectionOverflow { input, rank },
520 });
521 }
522 Ok((0..rank)
523 .map(|axis| u32::try_from(axis).expect("rank precheck keeps axes encodable"))
524 .collect())
525}
526
527fn merge_specialization_requirements(
528 current: &SpecializationRequirements,
529 minimum: &SpecializationRequirements,
530) -> SpecializationRequirements {
531 debug_assert_eq!(current.inputs().len(), minimum.inputs().len());
532 let inputs = current
533 .inputs()
534 .iter()
535 .zip(minimum.inputs())
536 .map(|(current, minimum)| merge_input_requirements(current, minimum))
537 .collect::<Vec<_>>();
538 SpecializationRequirements::new(inputs)
539}
540
541fn merge_input_requirements(
542 current: &InputSpecializationRequirements,
543 minimum: &InputSpecializationRequirements,
544) -> InputSpecializationRequirements {
545 let mut axes = current.concrete_dimensions().to_vec();
546 for axis in minimum.concrete_dimensions() {
547 if !axes.contains(axis) {
548 axes.push(*axis);
549 }
550 }
551 let layout = current.layout().max(minimum.layout());
552 let rank = current.specializes_rank()
553 || minimum.specializes_rank()
554 || !axes.is_empty()
555 || layout == LayoutSpecialization::ExactStrides;
556 let alignment = match (current.alignment_log2(), minimum.alignment_log2()) {
557 (Some(left), Some(right)) => Some(left.max(right)),
558 (Some(value), None) | (None, Some(value)) => Some(value),
559 (None, None) => None,
560 };
561 let mut builder = InputSpecializationRequirements::builder();
562 builder
563 .dtype(current.specializes_dtype() || minimum.specializes_dtype())
564 .rank(rank)
565 .concrete_dimensions(axes)
566 .placement(current.placement().max(minimum.placement()))
567 .layout(layout)
568 .alignment_log2(alignment);
569 builder
570 .build()
571 .expect("merged CUDA specialization requirements preserve builder invariants")
572}
573
574fn wrong_family_error(expected_kind: CudaPreparedKind, operation: &'static str) -> PrepareError {
575 PrepareError::ProviderContract {
576 source: ProviderContractError::WrongOperationFamily {
577 expected: expected_kind.core_capability(),
578 operation,
579 },
580 }
581}
582
583impl CudaPreparedKind {
584 fn core_capability(self) -> CoreCapabilityKind {
585 match self {
586 Self::Elementwise => CoreCapabilityKind::Elementwise,
587 Self::Reduction => CoreCapabilityKind::Reduction,
588 Self::Indexing => CoreCapabilityKind::Indexing,
589 Self::DotGeneral => CoreCapabilityKind::DotGeneral,
590 Self::Layout => CoreCapabilityKind::Layout,
591 }
592 }
593}
594
595fn core_operation_name(op: &CoreSemanticOp) -> &'static str {
596 match op {
597 CoreSemanticOp::Add => "add",
598 CoreSemanticOp::Sub => "sub",
599 CoreSemanticOp::Mul => "mul",
600 CoreSemanticOp::Neg => "neg",
601 CoreSemanticOp::Conj => "conj",
602 CoreSemanticOp::DotGeneral { .. } => "dot_general",
603 CoreSemanticOp::Transpose { .. } => "transpose",
604 CoreSemanticOp::Reshape { .. } => "reshape",
605 CoreSemanticOp::BroadcastInDim { .. } => "broadcast_in_dim",
606 CoreSemanticOp::Convert { .. } => "convert",
607 CoreSemanticOp::Constant { .. } => "constant",
608 CoreSemanticOp::ReduceSum { .. } => "reduce_sum",
609 CoreSemanticOp::ReduceSumSquares { .. } => "reduce_sum_squares",
610 CoreSemanticOp::Div => "div",
611 CoreSemanticOp::Rem => "rem",
612 CoreSemanticOp::Abs => "abs",
613 CoreSemanticOp::Sign => "sign",
614 CoreSemanticOp::Maximum => "maximum",
615 CoreSemanticOp::Minimum => "minimum",
616 CoreSemanticOp::Compare(_) => "compare",
617 CoreSemanticOp::Select => "select",
618 CoreSemanticOp::Clamp => "clamp",
619 CoreSemanticOp::Exp => "exp",
620 CoreSemanticOp::Log => "log",
621 CoreSemanticOp::Sin => "sin",
622 CoreSemanticOp::Cos => "cos",
623 CoreSemanticOp::Tanh => "tanh",
624 CoreSemanticOp::Sqrt => "sqrt",
625 CoreSemanticOp::Rsqrt => "rsqrt",
626 CoreSemanticOp::Pow => "pow",
627 CoreSemanticOp::Expm1 => "expm1",
628 CoreSemanticOp::Log1p => "log1p",
629 CoreSemanticOp::ExtractDiag { .. } => "extract_diag",
630 CoreSemanticOp::EmbedDiag { .. } => "embed_diag",
631 CoreSemanticOp::Tril { .. } => "tril",
632 CoreSemanticOp::Triu { .. } => "triu",
633 CoreSemanticOp::Gather(_) => "gather",
634 CoreSemanticOp::GatherDynamicSliceSizes { .. } => "gather_dynamic_slice_sizes",
635 CoreSemanticOp::Scatter(_) => "scatter",
636 CoreSemanticOp::Slice(_) => "slice",
637 CoreSemanticOp::DynamicSlice { .. } => "dynamic_slice",
638 CoreSemanticOp::DynamicUpdateSlice => "dynamic_update_slice",
639 CoreSemanticOp::Pad(_) => "pad",
640 CoreSemanticOp::Concatenate { .. } => "concatenate",
641 CoreSemanticOp::Reverse { .. } => "reverse",
642 CoreSemanticOp::ShapeOf { .. } => "shape_of",
643 CoreSemanticOp::DynamicTruncate { .. } => "dynamic_truncate",
644 CoreSemanticOp::PadToMatch { .. } => "pad_to_match",
645 CoreSemanticOp::ReduceProd { .. } => "reduce_prod",
646 CoreSemanticOp::ReduceMax { .. } => "reduce_max",
647 CoreSemanticOp::ReduceMin { .. } => "reduce_min",
648 _ => UNKNOWN_CORE_OPERATION,
649 }
650}
651
652fn checked_specialization_heap_retained_bytes(
653 specialization: &SpecializationProjection,
654) -> Option<usize> {
655 let requirements = specialization.requirements();
656 checked_sum([
657 requirements
658 .inputs()
659 .len()
660 .checked_mul(size_of::<InputSpecializationRequirements>())?,
661 checked_sum(
662 requirements
663 .inputs()
664 .iter()
665 .map(|input| size_of_val(input.concrete_dimensions())),
666 )?,
667 specialization
668 .inputs()
669 .len()
670 .checked_mul(size_of::<InputSpecializationProjection>())?,
671 checked_sum_options(
672 specialization
673 .inputs()
674 .iter()
675 .map(input_projection_retained_bytes),
676 )?,
677 ])
678}
679
680fn input_projection_retained_bytes(projection: &InputSpecializationProjection) -> Option<usize> {
681 size_of_val(projection.concrete_dimensions()).checked_add(match projection.layout() {
682 Some(LayoutProjection::ExactStrides(strides)) if strides.spilled() => {
683 size_of_val(strides.as_slice())
684 }
685 _ => 0,
686 })
687}
688
689fn checked_sum(values: impl IntoIterator<Item = usize>) -> Option<usize> {
690 values
691 .into_iter()
692 .try_fold(0usize, |sum, value| sum.checked_add(value))
693}
694
695fn checked_sum_options(values: impl IntoIterator<Item = Option<usize>>) -> Option<usize> {
696 values
697 .into_iter()
698 .try_fold(0usize, |sum, value| sum.checked_add(value?))
699}
700
701fn cache_owner_error(source: crate::Error) -> CacheOwnerError {
702 CacheOwnerError::new(Arc::new(source))
703}
704
705impl fmt::Display for CudaPreparedKind {
706 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
707 formatter.write_str(match self {
708 Self::Elementwise => "elementwise",
709 Self::Reduction => "reduction",
710 Self::Indexing => "indexing",
711 Self::DotGeneral => "dot_general",
712 Self::Layout => "layout",
713 })
714 }
715}