sim_lib_compute_cuda/
storage.rs1use std::{
4 any::Any,
5 sync::{Arc, OnceLock},
6};
7
8use sim_kernel::{DefaultFactory, Error, Factory, Result, Symbol, Value};
9use sim_lib_numbers_tensor::{Tensor, TensorLocation, TensorStorage};
10
11use crate::runtime::CudaDeviceBuffer;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct CudaAllocation {
16 pub id: usize,
18 pub bytes: u64,
20 pub operation: Symbol,
22}
23
24impl CudaAllocation {
25 fn id_symbol(&self) -> Symbol {
26 Symbol::qualified("compute.alloc.cuda", self.id.to_string())
27 }
28}
29
30pub struct CudaResidentStorage {
32 site: Symbol,
33 allocation: CudaAllocation,
34 shape: Arc<[usize]>,
35 dtype: Symbol,
36 backing: CudaBacking,
37 materialized: OnceLock<Result<Arc<dyn TensorStorage>>>,
38}
39
40enum CudaBacking {
41 Host(Arc<[Value]>),
42 Device(Arc<CudaDeviceBuffer>),
43}
44
45impl CudaResidentStorage {
46 pub fn new(
48 site: Symbol,
49 allocation: CudaAllocation,
50 shape: Vec<usize>,
51 dtype: Symbol,
52 cells: Arc<[Value]>,
53 ) -> Self {
54 Self {
55 site,
56 allocation,
57 shape: shape.into(),
58 dtype,
59 backing: CudaBacking::Host(cells),
60 materialized: OnceLock::new(),
61 }
62 }
63
64 pub(crate) fn from_device(
65 site: Symbol,
66 allocation: CudaAllocation,
67 shape: Vec<usize>,
68 dtype: Symbol,
69 buffer: Arc<CudaDeviceBuffer>,
70 ) -> Self {
71 Self {
72 site,
73 allocation,
74 shape: shape.into(),
75 dtype,
76 backing: CudaBacking::Device(buffer),
77 materialized: OnceLock::new(),
78 }
79 }
80
81 pub fn allocation(&self) -> &CudaAllocation {
83 &self.allocation
84 }
85
86 pub fn resident_tensor(&self) -> Option<Tensor> {
88 let storage = self.materialize().ok()?;
89 Tensor::from_storage(self.shape.to_vec(), self.dtype.clone(), storage).ok()
90 }
91
92 pub(crate) fn device_buffer(&self) -> Option<&Arc<CudaDeviceBuffer>> {
93 match &self.backing {
94 CudaBacking::Host(_) => None,
95 CudaBacking::Device(buffer) => Some(buffer),
96 }
97 }
98
99 fn materialized_storage(&self) -> Result<Arc<dyn TensorStorage>> {
100 let cells = match &self.backing {
101 CudaBacking::Host(cells) => Arc::clone(cells),
102 CudaBacking::Device(buffer) => buffer
103 .read()
104 .map_err(|error| Error::Eval(error.to_string()))?
105 .into_iter()
106 .map(|value| DefaultFactory.number_literal(self.dtype.clone(), value.to_string()))
107 .collect::<Result<Vec<_>>>()?
108 .into(),
109 };
110 Ok(Arc::new(BoxedCudaStorage::new(self.dtype.clone(), cells)))
111 }
112}
113
114impl TensorStorage for CudaResidentStorage {
115 fn dtype(&self) -> &Symbol {
116 &self.dtype
117 }
118
119 fn len(&self) -> usize {
120 match &self.backing {
121 CudaBacking::Host(cells) => cells.len(),
122 CudaBacking::Device(buffer) => buffer.len(),
123 }
124 }
125
126 fn location(&self) -> TensorLocation {
127 TensorLocation::Resident {
128 site: self.site.clone(),
129 allocation: self.allocation.id_symbol(),
130 }
131 }
132
133 fn cell(&self, index: usize) -> Result<Value> {
134 self.materialize()?.cell(index)
135 }
136
137 fn materialize(&self) -> Result<Arc<dyn TensorStorage>> {
138 self.materialized
139 .get_or_init(|| self.materialized_storage())
140 .clone()
141 }
142
143 fn as_any(&self) -> &dyn Any {
144 self
145 }
146}
147
148struct BoxedCudaStorage {
149 dtype: Symbol,
150 cells: Arc<[Value]>,
151}
152
153impl BoxedCudaStorage {
154 fn new(dtype: Symbol, cells: Arc<[Value]>) -> Self {
155 Self { dtype, cells }
156 }
157}
158
159impl TensorStorage for BoxedCudaStorage {
160 fn dtype(&self) -> &Symbol {
161 &self.dtype
162 }
163
164 fn len(&self) -> usize {
165 self.cells.len()
166 }
167
168 fn location(&self) -> TensorLocation {
169 TensorLocation::Host
170 }
171
172 fn cell(&self, index: usize) -> Result<Value> {
173 self.cells
174 .get(index)
175 .cloned()
176 .ok_or_else(|| Error::Eval("cuda tensor cell index was out of bounds".to_owned()))
177 }
178
179 fn materialize(&self) -> Result<Arc<dyn TensorStorage>> {
180 Ok(Arc::new(Self {
181 dtype: self.dtype.clone(),
182 cells: self.cells.clone(),
183 }))
184 }
185
186 fn as_any(&self) -> &dyn Any {
187 self
188 }
189}
190
191impl sim_kernel::Object for CudaAllocation {
192 fn display(&self, _cx: &mut sim_kernel::Cx) -> Result<String> {
193 Ok(format!("#<cuda-allocation {} {}b>", self.id, self.bytes))
194 }
195
196 fn as_any(&self) -> &dyn Any {
197 self
198 }
199}
200
201impl sim_kernel::ObjectCompat for CudaAllocation {
202 fn class(&self, _cx: &mut sim_kernel::Cx) -> Result<sim_kernel::ClassRef> {
203 DefaultFactory.class_stub(
204 sim_kernel::CORE_FUNCTION_CLASS_ID,
205 Symbol::qualified("compute", "CudaAllocation"),
206 )
207 }
208}