onnx_runtime_eager/
tensor.rs1use std::ffi::c_void;
23use std::sync::Arc;
24use std::sync::OnceLock;
25
26use onnx_runtime_ep_api::{DeviceBuffer, ExecutionProvider};
27use onnx_runtime_ep_cpu::CpuExecutionProvider;
28use onnx_runtime_ir::{DataType, DeviceId, TensorLayout};
29
30use crate::error::{EagerError, Result};
31
32fn shared_cpu_ep() -> Arc<dyn ExecutionProvider> {
37 static EP: OnceLock<Arc<CpuExecutionProvider>> = OnceLock::new();
38 EP.get_or_init(|| {
39 let mut ep = CpuExecutionProvider::new();
40 let _ = ep.initialize(&Default::default());
42 Arc::new(ep)
43 })
44 .clone()
45}
46
47fn host_bytes(buffer: &DeviceBuffer) -> &[u8] {
53 assert!(
54 buffer.device().is_host_accessible(),
55 "host_bytes on non-host device {:?}",
56 buffer.device()
57 );
58 if buffer.is_empty() {
59 return &[];
60 }
61 unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }
65}
66
67fn write_host(buffer: &mut DeviceBuffer, src: &[u8]) -> Result<()> {
69 assert!(
70 buffer.device().is_host_accessible(),
71 "write_host on non-host device {:?}",
72 buffer.device()
73 );
74 if src.len() > buffer.len() {
75 return Err(EagerError::Kernel(
76 onnx_runtime_ep_api::EpError::KernelFailed(format!(
77 "write_host: source {} bytes exceeds buffer {} bytes",
78 src.len(),
79 buffer.len()
80 )),
81 ));
82 }
83 if src.is_empty() {
84 return Ok(());
85 }
86 let dst = buffer.as_mut_ptr() as *mut u8;
87 unsafe {
91 std::ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
92 }
93 Ok(())
94}
95
96pub struct Tensor {
102 dtype: DataType,
103 shape: Vec<usize>,
104 layout: TensorLayout,
105 device: DeviceId,
106 buffer: Option<DeviceBuffer>,
108 allocator: Arc<dyn ExecutionProvider>,
110}
111
112impl Tensor {
113 pub fn from_raw_in(
118 allocator: Arc<dyn ExecutionProvider>,
119 dtype: DataType,
120 shape: Vec<usize>,
121 bytes: &[u8],
122 ) -> Result<Self> {
123 let numel: usize = shape.iter().product();
124 let expected = dtype.storage_bytes(numel);
125 if bytes.len() != expected {
126 return Err(EagerError::Kernel(
127 onnx_runtime_ep_api::EpError::KernelFailed(format!(
128 "Tensor::from_raw_in: {} bytes for shape {shape:?} dtype {dtype:?}, expected {expected}",
129 bytes.len()
130 )),
131 ));
132 }
133 let layout = TensorLayout::contiguous();
134 let mut buffer = allocator.allocate(expected.max(1), layout.alignment)?;
135 write_host(&mut buffer, bytes)?;
136 Ok(Self {
137 dtype,
138 shape,
139 layout,
140 device: buffer.device(),
141 buffer: Some(buffer),
142 allocator,
143 })
144 }
145
146 pub fn zeros_in(
151 allocator: Arc<dyn ExecutionProvider>,
152 dtype: DataType,
153 shape: Vec<usize>,
154 ) -> Result<Self> {
155 let numel: usize = shape.iter().product();
156 let bytes = vec![0u8; dtype.storage_bytes(numel)];
157 Self::from_raw_in(allocator, dtype, shape, &bytes)
158 }
159
160 pub fn from_raw(dtype: DataType, shape: Vec<usize>, bytes: &[u8]) -> Result<Self> {
162 Self::from_raw_in(shared_cpu_ep(), dtype, shape, bytes)
163 }
164
165 pub fn zeros(dtype: DataType, shape: Vec<usize>) -> Result<Self> {
167 Self::zeros_in(shared_cpu_ep(), dtype, shape)
168 }
169
170 pub fn from_f32(shape: &[usize], data: &[f32]) -> Result<Self> {
172 let mut bytes = Vec::with_capacity(data.len() * 4);
173 for v in data {
174 bytes.extend_from_slice(&v.to_le_bytes());
175 }
176 Self::from_raw(DataType::Float32, shape.to_vec(), &bytes)
177 }
178
179 pub fn from_i64(shape: &[usize], data: &[i64]) -> Result<Self> {
181 let mut bytes = Vec::with_capacity(data.len() * 8);
182 for v in data {
183 bytes.extend_from_slice(&v.to_le_bytes());
184 }
185 Self::from_raw(DataType::Int64, shape.to_vec(), &bytes)
186 }
187
188 pub fn dtype(&self) -> DataType {
190 self.dtype
191 }
192
193 pub fn shape(&self) -> &[usize] {
195 &self.shape
196 }
197
198 pub fn layout(&self) -> &TensorLayout {
200 &self.layout
201 }
202
203 pub fn device(&self) -> DeviceId {
205 self.device
206 }
207
208 pub fn numel(&self) -> usize {
210 self.shape.iter().product()
211 }
212
213 fn buffer(&self) -> &DeviceBuffer {
214 self.buffer
215 .as_ref()
216 .expect("Tensor buffer taken only in Drop")
217 }
218
219 pub(crate) fn device_ptr(&self) -> *const c_void {
222 self.buffer().as_ptr()
223 }
224
225 pub(crate) fn device_ptr_mut(&mut self) -> *mut c_void {
227 self.buffer
228 .as_mut()
229 .expect("Tensor buffer taken only in Drop")
230 .as_mut_ptr()
231 }
232
233 pub fn as_bytes(&self) -> &[u8] {
235 let n = self.dtype.storage_bytes(self.numel());
236 &host_bytes(self.buffer())[..n]
237 }
238
239 pub fn to_vec_f32(&self) -> Vec<f32> {
241 assert_eq!(
242 self.dtype,
243 DataType::Float32,
244 "to_vec_f32 on non-f32 tensor"
245 );
246 self.as_bytes()
247 .chunks_exact(4)
248 .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
249 .collect()
250 }
251
252 pub fn to_vec_i64(&self) -> Vec<i64> {
254 assert_eq!(self.dtype, DataType::Int64, "to_vec_i64 on non-i64 tensor");
255 self.as_bytes()
256 .chunks_exact(8)
257 .map(|c| i64::from_le_bytes(c.try_into().unwrap()))
258 .collect()
259 }
260}
261
262impl Clone for Tensor {
263 fn clone(&self) -> Self {
264 Self::from_raw_in(
265 self.allocator.clone(),
266 self.dtype,
267 self.shape.clone(),
268 self.as_bytes(),
269 )
270 .expect("Tensor::clone: re-allocation of identical bytes")
271 }
272}
273
274impl std::fmt::Debug for Tensor {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 f.debug_struct("Tensor")
277 .field("dtype", &self.dtype)
278 .field("shape", &self.shape)
279 .field("device", &self.device)
280 .finish()
281 }
282}
283
284impl Drop for Tensor {
285 fn drop(&mut self) {
286 if let Some(buffer) = self.buffer.take() {
287 let _ = self.allocator.deallocate(buffer);
291 }
292 }
293}