tenflowers_core/
buffer.rs1#![allow(clippy::result_large_err)]
2
3use crate::{Device, Result, TensorError};
4use std::sync::Arc;
5
6pub trait TensorBuffer: Send + Sync {
8 type Elem: Clone;
9
10 fn device(&self) -> &Device;
12
13 fn len(&self) -> usize;
15
16 fn is_empty(&self) -> bool {
18 self.len() == 0
19 }
20
21 fn size_bytes(&self) -> usize;
23
24 fn clone_buffer(&self) -> Result<Box<dyn TensorBuffer<Elem = Self::Elem>>>;
26
27 fn view(&self, offset: usize, len: usize) -> Result<Box<dyn TensorBuffer<Elem = Self::Elem>>>;
29
30 fn to_cpu(&self) -> Result<Vec<Self::Elem>>;
32
33 unsafe fn as_ptr(&self) -> *const Self::Elem;
39
40 unsafe fn as_mut_ptr(&mut self) -> *mut Self::Elem;
46}
47
48pub struct SharedBuffer<T> {
50 data: Arc<dyn TensorBuffer<Elem = T>>,
51 offset: usize,
52 len: usize,
53}
54
55impl<T: Clone + Send + Sync + 'static> SharedBuffer<T> {
56 pub fn new(buffer: Box<dyn TensorBuffer<Elem = T>>) -> Self {
58 let len = buffer.len();
59 Self {
60 data: Arc::from(buffer),
61 offset: 0,
62 len,
63 }
64 }
65
66 pub fn view(&self, offset: usize, len: usize) -> Result<Self> {
68 if offset + len > self.len {
69 return Err(TensorError::invalid_argument(format!(
70 "View out of bounds: offset={offset}, len={len}, buffer_len={}",
71 self.len
72 )));
73 }
74
75 Ok(Self {
76 data: Arc::clone(&self.data),
77 offset: self.offset + offset,
78 len,
79 })
80 }
81
82 pub fn ref_count(&self) -> usize {
84 Arc::strong_count(&self.data)
85 }
86}
87
88pub struct CpuBuffer<T> {
90 data: Vec<T>,
91 device: Device,
92}
93
94impl<T: Clone + Send + Sync> CpuBuffer<T> {
95 pub fn new(data: Vec<T>) -> Self {
96 Self {
97 data,
98 device: Device::Cpu,
99 }
100 }
101
102 pub fn zeros(len: usize) -> Self
103 where
104 T: Default,
105 {
106 Self {
107 data: vec![T::default(); len],
108 device: Device::Cpu,
109 }
110 }
111}
112
113impl<T: Clone + Send + Sync + 'static> TensorBuffer for CpuBuffer<T> {
114 type Elem = T;
115
116 fn device(&self) -> &Device {
117 &self.device
118 }
119
120 fn len(&self) -> usize {
121 self.data.len()
122 }
123
124 fn size_bytes(&self) -> usize {
125 self.data.len() * std::mem::size_of::<T>()
126 }
127
128 fn clone_buffer(&self) -> Result<Box<dyn TensorBuffer<Elem = Self::Elem>>> {
129 Ok(Box::new(Self {
130 data: self.data.clone(),
131 device: self.device,
132 }))
133 }
134
135 fn view(&self, offset: usize, len: usize) -> Result<Box<dyn TensorBuffer<Elem = Self::Elem>>> {
136 if offset + len > self.data.len() {
137 return Err(TensorError::invalid_argument(format!(
138 "View out of bounds: offset={offset}, len={len}, buffer_len={}",
139 self.data.len()
140 )));
141 }
142
143 Ok(Box::new(Self {
144 data: self.data[offset..offset + len].to_vec(),
145 device: self.device,
146 }))
147 }
148
149 fn to_cpu(&self) -> Result<Vec<Self::Elem>> {
150 Ok(self.data.clone())
151 }
152
153 unsafe fn as_ptr(&self) -> *const Self::Elem {
154 self.data.as_ptr()
155 }
156
157 unsafe fn as_mut_ptr(&mut self) -> *mut Self::Elem {
158 self.data.as_mut_ptr()
159 }
160}
161
162type PoolKey = (Device, usize);
163type PoolValue = Vec<Box<dyn std::any::Any + Send>>;
164type PoolMap = std::collections::HashMap<PoolKey, PoolValue>;
165
166pub struct MemoryPool {
168 pools: std::sync::Mutex<PoolMap>,
169 max_pool_size: usize,
170}
171
172impl MemoryPool {
173 pub fn new(max_pool_size: usize) -> Self {
174 Self {
175 pools: std::sync::Mutex::new(std::collections::HashMap::new()),
176 max_pool_size,
177 }
178 }
179
180 pub fn allocate<
182 T: Clone + Send + Sync + Default + bytemuck::Pod + bytemuck::Zeroable + 'static,
183 >(
184 &self,
185 device: Device,
186 len: usize,
187 ) -> Box<dyn TensorBuffer<Elem = T>> {
188 let key = (device, std::mem::size_of::<T>());
189 let mut pools = match self.pools.lock() {
190 Ok(g) => g,
191 Err(_) => {
192 return match device {
194 Device::Cpu => Box::new(CpuBuffer::zeros(len)),
195 #[cfg(feature = "gpu")]
196 Device::Gpu(id) => {
197 use crate::gpu::buffer::GpuBuffer;
198 match GpuBuffer::<T>::zeros(len, id) {
199 Ok(buf) => Box::new(buf),
200 Err(_) => Box::new(CpuBuffer::zeros(len)),
201 }
202 }
203 #[cfg(feature = "rocm")]
204 Device::Rocm(id) => {
205 use crate::gpu::buffer::GpuBuffer;
206 match GpuBuffer::<T>::zeros(len, id) {
207 Ok(buf) => Box::new(buf),
208 Err(_) => Box::new(CpuBuffer::zeros(len)),
209 }
210 }
211 };
212 }
213 };
214
215 if let Some(pool) = pools.get_mut(&key) {
216 for i in 0..pool.len() {
218 if let Some(buffer) = pool[i].downcast_ref::<CpuBuffer<T>>() {
219 if buffer.len() >= len {
220 let recycled = pool.swap_remove(i);
221 if let Ok(mut buffer) = recycled.downcast::<CpuBuffer<T>>() {
222 buffer.data.resize(len, T::default());
224 return buffer;
225 }
226 }
227 }
228 }
229 }
230
231 match device {
233 Device::Cpu => Box::new(CpuBuffer::zeros(len)),
234 #[cfg(feature = "gpu")]
235 Device::Gpu(id) => {
236 use crate::gpu::buffer::GpuBuffer;
237 match GpuBuffer::<T>::zeros(len, id) {
238 Ok(buf) => Box::new(buf),
239 Err(_) => Box::new(CpuBuffer::zeros(len)), }
241 }
242 #[cfg(feature = "rocm")]
243 Device::Rocm(id) => {
244 use crate::gpu::buffer::GpuBuffer;
245 match GpuBuffer::<T>::zeros(len, id) {
246 Ok(buf) => Box::new(buf),
247 Err(_) => Box::new(CpuBuffer::zeros(len)), }
249 }
250 }
251 }
252
253 pub fn deallocate<T: 'static>(&self, device: Device, buffer: Box<dyn std::any::Any + Send>) {
255 let key = (device, std::mem::size_of::<T>());
256 let mut pools = match self.pools.lock() {
257 Ok(g) => g,
258 Err(_) => return, };
260
261 let pool = pools.entry(key).or_default();
262 if pool.len() < self.max_pool_size {
263 pool.push(buffer);
264 }
265 }
266}
267
268lazy_static::lazy_static! {
270 pub static ref MEMORY_POOL: MemoryPool = MemoryPool::new(100);
271}