2019-06-27 00:32:32 -07:00
|
|
|
use rand::{thread_rng, Rng};
|
2019-11-07 19:48:33 -08:00
|
|
|
use std::sync::atomic::AtomicBool;
|
2019-06-27 00:32:32 -07:00
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
2019-12-11 11:58:40 -08:00
|
|
|
use std::sync::{Arc, Mutex, Weak};
|
2019-06-27 00:32:32 -07:00
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
struct RecyclerStats {
|
|
|
|
total: AtomicUsize,
|
2019-11-20 11:25:18 -08:00
|
|
|
freed: AtomicUsize,
|
2019-06-27 00:32:32 -07:00
|
|
|
reuse: AtomicUsize,
|
|
|
|
max_gc: AtomicUsize,
|
|
|
|
}
|
|
|
|
|
2019-12-11 11:58:40 -08:00
|
|
|
#[derive(Clone, Default)]
|
2019-06-27 00:32:32 -07:00
|
|
|
pub struct Recycler<T> {
|
2019-12-11 11:58:40 -08:00
|
|
|
recycler: Arc<RecyclerX<T>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RecyclerX<T> {
|
|
|
|
gc: Mutex<Vec<T>>,
|
|
|
|
stats: RecyclerStats,
|
2019-06-27 00:32:32 -07:00
|
|
|
id: usize,
|
|
|
|
}
|
|
|
|
|
2019-12-11 11:58:40 -08:00
|
|
|
impl<T: Default> Default for RecyclerX<T> {
|
|
|
|
fn default() -> RecyclerX<T> {
|
2019-06-27 00:32:32 -07:00
|
|
|
let id = thread_rng().gen_range(0, 1000);
|
|
|
|
trace!("new recycler..{}", id);
|
2019-12-11 11:58:40 -08:00
|
|
|
RecyclerX {
|
|
|
|
gc: Mutex::new(vec![]),
|
|
|
|
stats: RecyclerStats::default(),
|
2019-06-27 00:32:32 -07:00
|
|
|
id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Reset {
|
|
|
|
fn reset(&mut self);
|
2019-11-07 19:48:33 -08:00
|
|
|
fn warm(&mut self, size_hint: usize);
|
2019-12-11 11:58:40 -08:00
|
|
|
fn set_recycler(&mut self, recycler: Weak<RecyclerX<Self>>)
|
|
|
|
where
|
|
|
|
Self: std::marker::Sized;
|
2019-11-07 19:48:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref WARM_RECYCLERS: AtomicBool = AtomicBool::new(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enable_recycler_warming() {
|
|
|
|
WARM_RECYCLERS.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn warm_recyclers() -> bool {
|
|
|
|
WARM_RECYCLERS.load(Ordering::Relaxed)
|
2019-06-27 00:32:32 -07:00
|
|
|
}
|
|
|
|
|
2019-12-11 11:58:40 -08:00
|
|
|
impl<T: Default + Reset + Sized> Recycler<T> {
|
2019-11-07 19:48:33 -08:00
|
|
|
pub fn warmed(num: usize, size_hint: usize) -> Self {
|
|
|
|
let new = Self::default();
|
|
|
|
if warm_recyclers() {
|
|
|
|
let warmed_items: Vec<_> = (0..num)
|
|
|
|
.map(|_| {
|
|
|
|
let mut item = new.allocate("warming");
|
|
|
|
item.warm(size_hint);
|
|
|
|
item
|
|
|
|
})
|
|
|
|
.collect();
|
2019-12-11 11:58:40 -08:00
|
|
|
warmed_items
|
|
|
|
.into_iter()
|
|
|
|
.for_each(|i| new.recycler.recycle(i));
|
2019-11-07 19:48:33 -08:00
|
|
|
}
|
|
|
|
new
|
|
|
|
}
|
|
|
|
|
2019-06-27 00:32:32 -07:00
|
|
|
pub fn allocate(&self, name: &'static str) -> T {
|
|
|
|
let new = self
|
2019-12-11 11:58:40 -08:00
|
|
|
.recycler
|
2019-06-27 00:32:32 -07:00
|
|
|
.gc
|
|
|
|
.lock()
|
|
|
|
.expect("recycler lock in pb fn allocate")
|
|
|
|
.pop();
|
|
|
|
|
|
|
|
if let Some(mut x) = new {
|
2019-12-11 11:58:40 -08:00
|
|
|
self.recycler.stats.reuse.fetch_add(1, Ordering::Relaxed);
|
2019-06-27 00:32:32 -07:00
|
|
|
x.reset();
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2019-12-11 11:58:40 -08:00
|
|
|
let total = self.recycler.stats.total.fetch_add(1, Ordering::Relaxed);
|
2019-06-27 00:32:32 -07:00
|
|
|
trace!(
|
|
|
|
"allocating new: total {} {:?} id: {} reuse: {} max_gc: {}",
|
2019-11-20 11:25:18 -08:00
|
|
|
total,
|
2019-06-27 00:32:32 -07:00
|
|
|
name,
|
2019-12-11 11:58:40 -08:00
|
|
|
self.recycler.id,
|
|
|
|
self.recycler.stats.reuse.load(Ordering::Relaxed),
|
|
|
|
self.recycler.stats.max_gc.load(Ordering::Relaxed),
|
2019-06-27 00:32:32 -07:00
|
|
|
);
|
|
|
|
|
2019-12-11 11:58:40 -08:00
|
|
|
let mut t = T::default();
|
|
|
|
t.set_recycler(Arc::downgrade(&self.recycler));
|
|
|
|
t
|
2019-06-27 00:32:32 -07:00
|
|
|
}
|
2019-12-11 11:58:40 -08:00
|
|
|
}
|
2019-06-27 00:32:32 -07:00
|
|
|
|
2019-12-11 11:58:40 -08:00
|
|
|
impl<T: Default + Reset> RecyclerX<T> {
|
2019-06-27 00:32:32 -07:00
|
|
|
pub fn recycle(&self, x: T) {
|
|
|
|
let len = {
|
|
|
|
let mut gc = self.gc.lock().expect("recycler lock in pub fn recycle");
|
|
|
|
gc.push(x);
|
|
|
|
gc.len()
|
|
|
|
};
|
|
|
|
|
|
|
|
let max_gc = self.stats.max_gc.load(Ordering::Relaxed);
|
|
|
|
if len > max_gc {
|
|
|
|
// this is not completely accurate, but for most cases should be fine.
|
|
|
|
self.stats
|
|
|
|
.max_gc
|
|
|
|
.compare_and_swap(max_gc, len, Ordering::Relaxed);
|
|
|
|
}
|
2019-11-20 11:25:18 -08:00
|
|
|
let total = self.stats.total.load(Ordering::Relaxed);
|
|
|
|
let reuse = self.stats.reuse.load(Ordering::Relaxed);
|
|
|
|
let freed = self.stats.total.fetch_add(1, Ordering::Relaxed);
|
|
|
|
datapoint_debug!(
|
|
|
|
"recycler",
|
|
|
|
("gc_len", len as i64, i64),
|
|
|
|
("total", total as i64, i64),
|
|
|
|
("freed", freed as i64, i64),
|
|
|
|
("reuse", reuse as i64, i64),
|
|
|
|
);
|
2019-06-27 00:32:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
impl Reset for u64 {
|
|
|
|
fn reset(&mut self) {
|
|
|
|
*self = 10;
|
|
|
|
}
|
2019-11-07 19:48:33 -08:00
|
|
|
fn warm(&mut self, _size_hint: usize) {}
|
2019-12-11 11:58:40 -08:00
|
|
|
fn set_recycler(&mut self, _recycler: Weak<RecyclerX<Self>>) {}
|
2019-06-27 00:32:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_recycler() {
|
|
|
|
let recycler = Recycler::default();
|
|
|
|
let mut y: u64 = recycler.allocate("test_recycler1");
|
|
|
|
assert_eq!(y, 0);
|
|
|
|
y = 20;
|
|
|
|
let recycler2 = recycler.clone();
|
2019-12-11 11:58:40 -08:00
|
|
|
recycler2.recycler.recycle(y);
|
|
|
|
assert_eq!(recycler.recycler.gc.lock().unwrap().len(), 1);
|
2019-06-27 00:32:32 -07:00
|
|
|
let z = recycler.allocate("test_recycler2");
|
|
|
|
assert_eq!(z, 10);
|
2019-12-11 11:58:40 -08:00
|
|
|
assert_eq!(recycler.recycler.gc.lock().unwrap().len(), 0);
|
2019-06-27 00:32:32 -07:00
|
|
|
}
|
|
|
|
}
|