egglog_core_relations/pool/
mod.rs1use std::{
4 cell::{Cell, RefCell},
5 fmt,
6 hash::{Hash, Hasher},
7 mem::{self, ManuallyDrop},
8 ops::{Deref, DerefMut},
9 ptr,
10 rc::Rc,
11};
12
13use crate::{
14 AtomId,
15 free_join::execute::TrieNode,
16 numeric_id::{DenseIdMap, IdVec},
17};
18use fixedbitset::FixedBitSet;
19use hashbrown::HashTable;
20
21use crate::{
22 ColumnId, RowId,
23 action::{Instr, PredictedVals},
24 common::{HashMap, HashSet, IndexMap, IndexSet, ShardId, Value},
25 free_join::execute::SortedColumnIndex,
26 hash_index::{BufferedSubset, TableEntry},
27 offsets::SortedOffsetVector,
28 table::TableEntry as SwTableEntry,
29 table_spec::Constraint,
30};
31
32#[cfg(test)]
33mod tests;
34
35pub trait Clear: Default {
37 fn clear(&mut self);
41 fn reuse(&self) -> bool {
43 true
44 }
45 fn bytes(&self) -> usize;
47}
48
49impl<T> Clear for Vec<T> {
50 fn clear(&mut self) {
51 self.clear()
52 }
53 fn reuse(&self) -> bool {
54 self.capacity() > 256
55 }
56 fn bytes(&self) -> usize {
57 self.capacity() * mem::size_of::<T>()
58 }
59}
60
61impl<T: Clear> Clear for Rc<T> {
62 fn clear(&mut self) {
63 Rc::get_mut(self).unwrap().clear()
64 }
65 fn reuse(&self) -> bool {
66 Rc::strong_count(self) == 1 && Rc::weak_count(self) == 0
67 }
68 fn bytes(&self) -> usize {
69 mem::size_of::<T>()
70 }
71}
72
73impl<T: Clear> Clone for Pooled<Rc<T>>
74where
75 Rc<T>: InPoolSet<PoolSet>,
76{
77 fn clone(&self) -> Self {
78 Pooled {
79 data: self.data.clone(),
80 }
81 }
82}
83
84impl<T> Clear for HashSet<T> {
85 fn clear(&mut self) {
86 self.clear()
87 }
88 fn reuse(&self) -> bool {
89 self.capacity() > 0
90 }
91 fn bytes(&self) -> usize {
92 self.capacity() * mem::size_of::<T>()
93 }
94}
95
96impl<T> Clear for HashTable<T> {
97 fn clear(&mut self) {
98 self.clear()
99 }
100 fn reuse(&self) -> bool {
101 self.capacity() > 0
102 }
103 fn bytes(&self) -> usize {
104 self.capacity() * mem::size_of::<T>()
105 }
106}
107
108impl<K, V> Clear for HashMap<K, V> {
109 fn clear(&mut self) {
110 self.clear()
111 }
112 fn reuse(&self) -> bool {
113 self.capacity() > 0
114 }
115 fn bytes(&self) -> usize {
116 self.capacity() * mem::size_of::<(K, V)>()
117 }
118}
119
120impl<K, V> Clear for IndexMap<K, V> {
121 fn clear(&mut self) {
122 self.clear()
123 }
124 fn reuse(&self) -> bool {
125 self.capacity() > 0
126 }
127 fn bytes(&self) -> usize {
128 self.capacity() * (mem::size_of::<u64>() + mem::size_of::<(K, V)>())
129 }
130}
131
132impl<T> Clear for IndexSet<T> {
133 fn clear(&mut self) {
134 self.clear()
135 }
136 fn reuse(&self) -> bool {
137 self.capacity() > 0
138 }
139 fn bytes(&self) -> usize {
140 self.capacity() * (mem::size_of::<u64>() + mem::size_of::<T>())
141 }
142}
143
144impl Clear for FixedBitSet {
145 fn clear(&mut self) {
146 self.clone_from(&Default::default());
147 }
148 fn reuse(&self) -> bool {
149 !self.is_empty()
150 }
151 fn bytes(&self) -> usize {
152 self.len() / 8
153 }
154}
155
156impl<K, V> Clear for IdVec<K, V> {
157 fn clear(&mut self) {
158 self.clear()
159 }
160 fn reuse(&self) -> bool {
161 self.capacity() > 0
162 }
163 fn bytes(&self) -> usize {
164 self.capacity() * mem::size_of::<V>()
165 }
166}
167
168struct PoolState<T> {
169 data: Vec<T>,
170 bytes: usize,
171 limit: usize,
172}
173
174impl<T: Clear> PoolState<T> {
175 fn new(limit: usize) -> Self {
176 PoolState {
177 data: Vec::new(),
178 bytes: 0,
179 limit,
180 }
181 }
182
183 fn push(&mut self, mut item: T) {
184 if !item.reuse() {
185 return;
186 }
187 if self.bytes + item.bytes() > self.limit {
188 return;
189 }
190 item.clear();
191 self.bytes += item.bytes();
192 self.data.push(item);
193 }
194
195 fn pop(&mut self) -> T {
196 if let Some(got) = self.data.pop() {
197 self.bytes -= got.bytes();
198 got
199 } else {
200 Default::default()
201 }
202 }
203
204 fn clear_and_shrink(&mut self) {
205 self.data.clear();
206 self.bytes = 0;
207 self.data.shrink_to_fit();
208 }
209}
210
211pub struct Pool<T> {
213 data: Rc<RefCell<PoolState<T>>>,
214}
215
216impl<T> Clone for Pool<T> {
217 fn clone(&self) -> Self {
218 Pool {
219 data: self.data.clone(),
220 }
221 }
222}
223
224impl<T: Clear> Default for Pool<T> {
225 fn default() -> Self {
226 Pool {
227 data: Rc::new(RefCell::new(PoolState::new(usize::MAX))),
228 }
229 }
230}
231
232impl<T: Clear + InPoolSet<PoolSet>> Pool<T> {
233 pub(crate) fn new(limit: usize) -> Pool<T> {
234 Pool {
235 data: Rc::new(RefCell::new(PoolState::new(limit))),
236 }
237 }
238 pub(crate) fn get(&self) -> Pooled<T> {
240 let empty = self.data.borrow_mut().pop();
241
242 Pooled {
243 data: ManuallyDrop::new(empty),
244 }
245 }
246
247 pub(crate) fn clear(&self) {
249 let mut data_mut = self.data.borrow_mut();
250 data_mut.clear_and_shrink();
251 }
252}
253
254pub struct Pooled<T: Clear + InPoolSet<PoolSet>> {
257 data: ManuallyDrop<T>,
258}
259
260impl<T: Clear + InPoolSet<PoolSet>> Default for Pooled<T> {
261 fn default() -> Self {
262 with_pool_set(|ps| ps.get::<T>())
263 }
264}
265
266impl<T: Clear + fmt::Debug + InPoolSet<PoolSet>> fmt::Debug for Pooled<T> {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 let data: &T = &self.data;
269 data.fmt(f)
270 }
271}
272impl<T: Clear + PartialEq + InPoolSet<PoolSet>> PartialEq for Pooled<T> {
273 fn eq(&self, other: &Self) -> bool {
274 <T as PartialEq>::eq(&self.data, &other.data)
276 }
277}
278
279impl<T: Clear + InPoolSet<PoolSet> + Eq> Eq for Pooled<T> {}
280
281impl<T: Clear + Hash + InPoolSet<PoolSet>> Hash for Pooled<T> {
282 fn hash<H: Hasher>(&self, state: &mut H) {
283 self.data.hash(state)
284 }
285}
286
287impl<T: Clear + InPoolSet<PoolSet> + 'static> Pooled<T> {
288 pub(crate) fn refresh(this: &mut Pooled<T>) {
297 this.data.clear();
298 if this.data.reuse() {
299 return;
300 }
301 let pool = with_pool_set(|ps| ps.get_pool::<T>());
302 let mut other = pool.data.borrow_mut().pop();
303 if !other.reuse() {
304 return;
305 }
306 let slot: &mut T = &mut this.data;
307 mem::swap(slot, &mut other);
308 }
309
310 pub(crate) fn into_inner(this: Pooled<T>) -> T {
311 let inner = unsafe { ptr::read(&this.data) };
314 mem::forget(this);
315 ManuallyDrop::into_inner(inner)
316 }
317
318 pub(crate) fn new(data: T) -> Pooled<T> {
319 Pooled {
320 data: ManuallyDrop::new(data),
321 }
322 }
323}
324
325impl<T: Clear + Clone + InPoolSet<PoolSet>> Pooled<T> {
326 pub(crate) fn cloned(this: &Pooled<T>) -> Pooled<T> {
327 let mut res = with_pool_set(|ps| ps.get::<T>());
328 res.clone_from(this);
329 res
330 }
331}
332
333impl<T: Clear + InPoolSet<PoolSet>> Drop for Pooled<T> {
334 fn drop(&mut self) {
335 let reuse = self.data.reuse();
336 if !reuse {
337 unsafe { ManuallyDrop::drop(&mut self.data) };
340 return;
341 }
342 self.data.clear();
343 let t: &T = &self.data;
344 with_pool_set(|ps| {
346 T::with_pool(ps, |pool| {
347 pool.data.borrow_mut().push(unsafe { ptr::read(t) })
348 })
349 });
350 }
351}
352
353impl<T: Clear + InPoolSet<PoolSet>> Deref for Pooled<T> {
354 type Target = T;
355
356 fn deref(&self) -> &T {
357 &self.data
358 }
359}
360
361impl<T: Clear + InPoolSet<PoolSet>> DerefMut for Pooled<T> {
362 fn deref_mut(&mut self) -> &mut T {
363 &mut self.data
364 }
365}
366
367pub trait InPoolSet<PoolSet>
370where
371 Self: Sized + Clear,
372{
373 fn with_pool<R>(pool_set: &PoolSet, f: impl FnOnce(&Pool<Self>) -> R) -> R;
374}
375
376macro_rules! pool_set {
377 ($vis:vis $name:ident { $($ident:ident : $ty:ty [ $bytes:expr ],)* }) => {
378 $vis struct $name {
379 $(
380 $ident: Pool<$ty>,
381 )*
382 }
383
384 impl Default for $name {
385 fn default() -> Self {
386 $name {
387 $(
388 $ident: Pool::new($bytes),
389 )*
390 }
391 }
392 }
393
394 impl $name {
395 $vis fn get_pool<T: InPoolSet<Self>>(&self) -> Pool<T> {
396 T::with_pool(self, Pool::clone)
397 }
398
399 $vis fn get<T: InPoolSet<Self> + Default>(&self) -> Pooled<T> {
400 T::with_pool(self, |pool| pool.get())
401 }
402 $vis fn clear(&self) {
403 $( self.$ident.clear(); )*
404 }
405 }
406
407 $(
408 impl InPoolSet<$name> for $ty {
409 fn with_pool<R>(pool_set: &$name, f: impl FnOnce(&Pool<Self>) -> R) -> R {
410 f(&pool_set.$ident)
411 }
412 }
413 )*
414 }
415}
416
417pool_set! {
426 pub PoolSet {
427 vec_vals: Vec<Value> [ 1 << 25 ],
428 vec_cell_vals: Vec<Cell<Value>> [ 1 << 25 ],
429 rows: Vec<RowId> [ 1 << 25 ],
432 offset_vec: SortedOffsetVector [ 1 << 20 ],
433 column_index: IndexMap<Value, BufferedSubset> [ 1 << 20 ],
434 constraints: Vec<Constraint> [ 1 << 20 ],
435 bitsets: FixedBitSet [ 1 << 20 ],
436 instrs: Vec<Instr> [ 1 << 20 ],
437 tuple_indexes: HashTable<TableEntry<BufferedSubset>> [ 1 << 20 ],
438 staged_outputs: HashTable<SwTableEntry> [ 1 << 25 ],
439 predicted_vals: PredictedVals [ 1 << 20 ],
440 shard_hist: DenseIdMap<ShardId, usize> [ 1 << 20 ],
441 instr_indexes: Vec<u32> [ 1 << 20 ],
442 cached_subsets: IdVec<ColumnId, std::sync::OnceLock<std::sync::Arc<SortedColumnIndex>>> [ 4 << 20 ],
443 intersected_on: DenseIdMap<AtomId, i64> [ 1 << 20 ],
444
445 cached_child: IdVec<ColumnId, std::sync::RwLock<HashMap<Value, (std::sync::Arc<TrieNode>, Box<[Constraint]>)>>> [ 1 << 20 ],
446 }
447}
448
449pub(crate) fn with_pool_set<R>(f: impl FnOnce(&PoolSet) -> R) -> R {
451 POOL_SET.with(|pool_set| f(pool_set))
452}
453
454thread_local! {
455 static POOL_SET: ManuallyDrop<PoolSet> = Default::default();
464}