extern crate crossbeam;
extern crate nn;
extern crate parking_lot;
use crossbeam::sync::SegQueue;
use nn::NN;
use parking_lot::{Mutex, MutexGuard};
use std::{cmp, f64};
use std::collections::{BinaryHeap, HashMap};
type Tick = u32;
pub type Id = u64;
struct Block {
last_used: [Tick; 2],
instated: Tick,
times_used: u32,
}
impl Block {
fn as_vec(&self, id: Id) -> Vec<f64> {
vec![id as f64, self.instated as f64, self.last_used[0] as f64, self.last_used[1] as f64,
self.times_used as f64]
}
}
#[derive(PartialEq)]
struct Prediction {
id: Id,
prediction: f64,
}
impl cmp::Ord for Prediction {
fn cmp(&self, other: &Prediction) -> cmp::Ordering {
if self.prediction < other.prediction {
cmp::Ordering::Less
} else {
cmp::Ordering::Greater
}
}
}
impl cmp::PartialOrd for Prediction {
fn partial_cmp(&self, other: &Prediction) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl cmp::Eq for Prediction {}
pub struct ColdIter {
heap: BinaryHeap<Prediction>,
}
impl Iterator for ColdIter {
type Item = Id;
fn next(&mut self) -> Option<Id> {
self.heap.pop().map(|Prediction { id, .. }| id)
}
}
pub struct Cache {
blocks: HashMap<Id, Block>,
nn: NN,
clock: Tick,
}
impl Cache {
fn tick(&mut self) {
self.clock += 1;
}
pub fn new() -> Cache {
Cache {
blocks: HashMap::new(),
nn: NN::new(&[5, 6, 1]),
clock: 0,
}
}
pub fn touch(&mut self, id: Id) {
{
let block = self.blocks.get_mut(&id).unwrap();
let goal = (self.clock as f64 * 0.01).tanh();
self.nn.train(&[(block.as_vec(id), vec![goal])]);
block.last_used[0] = block.last_used[1];
block.last_used[1] = self.clock;
block.times_used += 1;
}
self.tick();
}
pub fn insert(&mut self, id: Id) {
self.blocks.insert(id, Block {
last_used: [!0; 2],
instated: self.clock,
times_used: 0,
});
}
pub fn remove(&mut self, id: Id) {
self.blocks.remove(&id);
}
pub fn cold(&mut self) -> ColdIter {
let mut heap = BinaryHeap::new();
for (&id, block) in self.blocks.iter() {
let prediction = self.nn.run(&block.as_vec(id))[0];
heap.push(Prediction {
id: id,
prediction: prediction,
});
}
ColdIter {
heap: heap,
}
}
pub fn trim(&mut self, to: usize) -> ::std::iter::Take<ColdIter> {
self.cold().take(self.blocks.len() - to)
}
}
enum CacheOperation {
Insert(Id),
Remove(Id),
Touch(Id),
}
pub struct ConcurrentCache {
inner: Mutex<Cache>,
queue: SegQueue<CacheOperation>,
}
impl ConcurrentCache {
pub fn new() -> ConcurrentCache {
ConcurrentCache {
inner: Mutex::new(Cache::new()),
queue: SegQueue::new(),
}
}
pub fn lock(&self) -> MutexGuard<Cache> {
let mut lock = self.inner.lock();
while let Some(op) = self.queue.try_pop() {
match op {
CacheOperation::Insert(id) => lock.insert(id),
CacheOperation::Remove(id) => lock.remove(id),
CacheOperation::Touch(id) => lock.touch(id),
}
}
lock
}
pub fn insert(&mut self, id: Id) {
self.queue.push(CacheOperation::Insert(id));
}
pub fn remove(&mut self, id: Id) {
self.queue.push(CacheOperation::Remove(id));
}
pub fn touch(&mut self, id: Id) {
self.queue.push(CacheOperation::Touch(id));
}
}